├── .editorconfig ├── .github ├── maven-cd-settings.xml ├── maven-ci-settings.xml └── workflows │ ├── ci-4.x.yml │ ├── ci-5.x-stable.yml │ ├── ci-5.x.yml │ ├── ci-matrix-5.x.yml │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── LICENSE ├── LICENSE.txt ├── README.adoc ├── pom.xml ├── vertx-infinispan ├── pom.xml └── src │ ├── main │ ├── asciidoc │ │ └── index.adoc │ ├── java │ │ ├── examples │ │ │ ├── Examples.java │ │ │ └── package-info.java │ │ └── io │ │ │ └── vertx │ │ │ └── ext │ │ │ └── cluster │ │ │ └── infinispan │ │ │ ├── ClusterHealthCheck.java │ │ │ ├── InfinispanAsyncMap.java │ │ │ ├── InfinispanClusterManager.java │ │ │ ├── impl │ │ │ ├── CloseableIteratorCollectionStream.java │ │ │ ├── ClusterHealthCheckImpl.java │ │ │ ├── DataConverter.java │ │ │ ├── InfinispanAsyncMapImpl.java │ │ │ ├── InfinispanCounter.java │ │ │ ├── InfinispanLock.java │ │ │ ├── SubsCacheHelper.java │ │ │ ├── SubsOpSerializer.java │ │ │ └── Throttling.java │ │ │ └── package-info.java │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── io.vertx.core.spi.VertxServiceProvider │ │ └── default-infinispan.xml │ └── test │ ├── java │ └── io │ │ └── vertx │ │ ├── Lifecycle.java │ │ ├── LoggingTestWatcher.java │ │ ├── core │ │ ├── InfinispanComplexHATest.java │ │ ├── InfinispanHATest.java │ │ ├── eventbus │ │ │ ├── InfinispanClusteredEventBusTest.java │ │ │ ├── InfinispanFaultToleranceTest.java │ │ │ └── InfinispanNodeInfoTest.java │ │ ├── servicediscovery │ │ │ └── impl │ │ │ │ └── InfinispanDiscoveryImplClusteredTest.java │ │ └── shareddata │ │ │ ├── InfinispanClusteredAsyncMapTest.java │ │ │ ├── InfinispanClusteredAsynchronousLockTest.java │ │ │ └── InfinispanClusteredSharedCounterTest.java │ │ ├── ext │ │ ├── cluster │ │ │ └── infinispan │ │ │ │ ├── ClusterHealthCheckTest.java │ │ │ │ └── impl │ │ │ │ └── ThrottlingTest.java │ │ └── web │ │ │ └── sstore │ │ │ └── InfinispanClusteredSessionHandlerTest.java │ │ └── it │ │ └── core │ │ └── ServiceProviderTest.java │ └── resources │ ├── jgroups.xml │ └── logback-test.xml └── vertx-web-sstore-infinispan ├── pom.xml └── src ├── main ├── asciidoc │ └── web-sstore-infinispan.adoc ├── java │ ├── examples │ │ ├── Examples.java │ │ └── package-info.java │ └── io │ │ └── vertx │ │ └── ext │ │ └── web │ │ └── sstore │ │ └── infinispan │ │ ├── InfinispanSessionStore.java │ │ ├── impl │ │ └── InfinispanSessionStoreImpl.java │ │ └── package-info.java └── resources │ └── META-INF │ └── services │ └── io.vertx.ext.web.sstore.SessionStore └── test ├── java └── io │ └── vertx │ └── ext │ └── web │ └── sstore │ └── infinispan │ └── impl │ └── InfinispanSessionHandlerTest.java └── resources └── identities.batch /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/maven-cd-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | vertx-snapshots-repository 24 | ${env.VERTX_NEXUS_USERNAME} 25 | ${env.VERTX_NEXUS_PASSWORD} 26 | 27 | 28 | 29 | 30 | 31 | google-mirror 32 | 33 | true 34 | 35 | 36 | 37 | google-maven-central 38 | GCS Maven Central mirror EU 39 | https://maven-central.storage-download.googleapis.com/maven2/ 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | google-maven-central 51 | GCS Maven Central mirror 52 | https://maven-central.storage-download.googleapis.com/maven2/ 53 | 54 | true 55 | 56 | 57 | false 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.github/maven-ci-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | google-mirror 24 | 25 | true 26 | 27 | 28 | 29 | google-maven-central 30 | GCS Maven Central mirror EU 31 | https://maven-central.storage-download.googleapis.com/maven2/ 32 | 33 | true 34 | 35 | 36 | false 37 | 38 | 39 | 40 | 41 | 42 | google-maven-central 43 | GCS Maven Central mirror 44 | https://maven-central.storage-download.googleapis.com/maven2/ 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/ci-4.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-infinispan (4.x) 2 | on: 3 | schedule: 4 | - cron: '0 4 * * *' 5 | jobs: 6 | CI: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest] 10 | jdk: [8,11] 11 | profile: [ISPN-13, ISPN-14, ISPN-14-jakarta] 12 | exclude: 13 | - jdk: 8 14 | profile: ISPN-14 15 | - jdk: 8 16 | profile: ISPN-14-jakarta 17 | - jdk: 11 18 | profile: ISPN-13 19 | uses: ./.github/workflows/ci.yml 20 | with: 21 | branch: 4.x 22 | jdk: ${{ matrix.jdk }} 23 | os: ${{ matrix.os }} 24 | secrets: inherit 25 | Deploy: 26 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 27 | needs: CI 28 | uses: ./.github/workflows/deploy.yml 29 | with: 30 | branch: 4.x 31 | jdk: 8 32 | secrets: inherit 33 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x-stable.yml: -------------------------------------------------------------------------------- 1 | name: vertx-infinispan (5.x-stable) 2 | on: 3 | push: 4 | branches: 5 | - '5.[0-9]+' 6 | pull_request: 7 | branches: 8 | - '5.[0-9]+' 9 | schedule: 10 | - cron: '0 6 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-infinispan (5.x) 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | schedule: 10 | - cron: '0 5 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-matrix-5.x.yml: -------------------------------------------------------------------------------- 1 | name: CI matrix (5.x) 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jobs: 9 | CI: 10 | strategy: 11 | matrix: 12 | include: 13 | - os: ubuntu-latest 14 | jdk: 11 15 | uses: ./.github/workflows/ci.yml 16 | with: 17 | branch: ${{ inputs.branch }} 18 | jdk: ${{ matrix.jdk }} 19 | os: ${{ matrix.os }} 20 | secrets: inherit 21 | Deploy: 22 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 23 | needs: CI 24 | uses: ./.github/workflows/deploy.yml 25 | with: 26 | branch: ${{ inputs.branch }} 27 | jdk: 11 28 | secrets: inherit 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | os: 12 | default: ubuntu-latest 13 | type: string 14 | profile: 15 | default: ISPN-13 16 | type: string 17 | jobs: 18 | Test: 19 | name: Run tests 20 | runs-on: ${{ inputs.os }} 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v2 24 | with: 25 | ref: ${{ inputs.branch }} 26 | - name: Install JDK 27 | uses: actions/setup-java@v2 28 | with: 29 | java-version: ${{ inputs.jdk }} 30 | distribution: temurin 31 | - name: Run tests 32 | run: mvn -s .github/maven-ci-settings.xml -B -DtestLogLevel=OFF test 33 | - uses: actions/upload-artifact@v4 34 | if: ${{ always() }} 35 | with: 36 | name: heap-dump 37 | path: target/*.hprof 38 | Test-ISPN14: 39 | if: ${{ startsWith(github.event.inputs.profile, 'ISPN-14') && github.event.inputs.branch == '4.x' }} 40 | name: Run tests 41 | runs-on: ${{ inputs.os }} 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v2 45 | - name: Install JDK 46 | uses: actions/setup-java@v2 47 | with: 48 | java-version: ${{ inputs.jdk }} 49 | distribution: temurin 50 | - name: Run tests 51 | run: mvn -P ${{ inputs.profile }} -s .github/maven-ci-settings.xml -B -DtestLogLevel=OFF test 52 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | jobs: 12 | Deploy: 13 | name: Deploy to OSSRH 14 | runs-on: ubuntu-latest 15 | env: 16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }} 17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ inputs.branch }} 23 | - name: Install JDK 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: ${{ inputs.jdk }} 27 | distribution: temurin 28 | - name: Get project version 29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV 30 | - name: Maven deploy 31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }} 32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gradle 3 | .idea 4 | .classpath 5 | .project 6 | .settings 7 | .yardoc 8 | .yardopts 9 | build 10 | target 11 | out 12 | *.iml 13 | *.ipr 14 | *.iws 15 | test-output 16 | Scratch.java 17 | ScratchTest.java 18 | test-results 19 | test-tmp 20 | *.class 21 | ScratchPad.java 22 | src/main/resources/ext-js/*.js 23 | src/main/java/io/vertx/java/**/*.java 24 | src/main/groovy/io/vertx/groovy/**/*.groovy 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Infinispan Cluster Manager 2 | 3 | image:https://github.com/vert-x3/vertx-infinispan/actions/workflows/ci-5.x.yml/badge.svg["Build Status (5.x)",link="https://github.com/vert-x3/vertx-infinispan/actions/workflows/ci-5.x.yml"] 4 | image:https://github.com/vert-x3/vertx-infinispan/actions/workflows/ci-4.x.yml/badge.svg["Build Status (4.x)",link="https://github.com/vert-x3/vertx-infinispan/actions/workflows/ci-4.x.yml"] 5 | 6 | This is a cluster manager implementation for Vert.x that uses http://infinispan.org[Infinispan]. 7 | 8 | Please see the in-source asciidoc documentation or the main documentation on the web-site for a full description 9 | of this component: 10 | 11 | * link:http://vertx.io/docs/vertx-infinispan/java/[web-site docs] 12 | * link:vertx-infinispan/src/main/asciidoc/index.adoc[in-source docs] 13 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 4.0.0 18 | 19 | 20 | io.vertx 21 | vertx5-parent 22 | 12 23 | 24 | 25 | vertx-infinispan-parent 26 | 5.1.0-SNAPSHOT 27 | pom 28 | 29 | 30 | scm:git:git@github.com:vert-x3/vertx-infinispan.git 31 | scm:git:git@github.com:vert-x3/vertx-infinispan.git 32 | git@github.com:vert-x3/vertx-infinispan.git 33 | 34 | 35 | 36 | 3.0.13 37 | 14.0.25.Final 38 | 39 | 40 | 41 | 42 | 43 | io.vertx 44 | vertx-dependencies 45 | ${project.version} 46 | pom 47 | import 48 | 49 | 50 | 51 | 52 | 53 | vertx-infinispan 54 | vertx-web-sstore-infinispan 55 | 56 | 57 | 58 | 59 | 60 | 61 | maven-compiler-plugin 62 | 63 | 64 | default-compile 65 | 66 | 67 | 68 | io.vertx 69 | vertx-codegen 70 | processor 71 | 72 | 73 | io.vertx 74 | vertx-docgen-processor 75 | processor 76 | 77 | 78 | 79 | -Adocgen.source=${vertx.asciidoc.sources.dir} 80 | -Adocgen.output=${project.build.directory}/asciidoc/java 81 | -Amaven.groupId=${project.groupId} 82 | -Amaven.artifactId=${project.artifactId} 83 | -Amaven.version=${project.version} 84 | -Ainfinispan.version=${infinispan.version} 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | maven-assembly-plugin 95 | 96 | 97 | package-docs 98 | 99 | single 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /vertx-infinispan/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 4.0.0 21 | 22 | 23 | io.vertx 24 | vertx-infinispan-parent 25 | 5.1.0-SNAPSHOT 26 | 27 | 28 | vertx-infinispan 29 | 30 | Vert.x Infinispan Cluster Manager 31 | 32 | 33 | 34 | 35 | io.vertx 36 | vertx-core 37 | 38 | 39 | org.infinispan 40 | infinispan-core-jakarta 41 | ${infinispan.version} 42 | 43 | 44 | io.reactivex.rxjava3 45 | rxjava 46 | 47 | 48 | org.wildfly.common 49 | wildfly-common 50 | 51 | 52 | caffeine 53 | com.github.ben-manes.caffeine 54 | 55 | 56 | 57 | 58 | org.wildfly.common 59 | wildfly-common 60 | 1.5.1.Final 61 | 62 | 63 | org.infinispan 64 | infinispan-multimap-jakarta 65 | ${infinispan.version} 66 | 67 | 68 | 69 | com.github.ben-manes.caffeine 70 | caffeine 71 | 2.8.0 72 | 73 | 74 | 75 | org.checkerframework 76 | checker-qual 77 | 78 | 79 | 80 | com.google.errorprone 81 | error_prone_annotations 82 | 83 | 84 | 85 | 86 | org.infinispan 87 | infinispan-clustered-lock 88 | ${infinispan.version} 89 | 90 | 91 | org.infinispan 92 | infinispan-core 93 | 94 | 95 | 96 | 97 | org.infinispan 98 | infinispan-clustered-counter 99 | ${infinispan.version} 100 | 101 | 102 | org.infinispan 103 | infinispan-core 104 | 105 | 106 | 107 | 108 | 109 | io.reactivex.rxjava3 110 | rxjava 111 | ${rxjava3.version} 112 | 113 | 114 | io.reactivestreams 115 | reactive-streams 116 | 117 | 118 | 119 | 120 | org.reactivestreams 121 | reactive-streams 122 | 1.0.3 123 | 124 | 125 | 126 | io.vertx 127 | vertx-codegen-api 128 | true 129 | 130 | 131 | io.vertx 132 | vertx-codegen-json 133 | true 134 | 135 | 136 | io.vertx 137 | vertx-docgen-api 138 | true 139 | 140 | 141 | 142 | io.vertx 143 | vertx-health-check 144 | true 145 | 146 | 147 | io.vertx 148 | vertx-web 149 | true 150 | 151 | 152 | 153 | junit 154 | junit 155 | 4.13.1 156 | test 157 | 158 | 159 | io.vertx 160 | vertx-core 161 | test-jar 162 | test 163 | 164 | 165 | io.vertx 166 | vertx-web 167 | test-jar 168 | test 169 | 170 | 171 | io.vertx 172 | vertx-service-discovery 173 | test 174 | 175 | 176 | io.vertx 177 | vertx-service-proxy 178 | test 179 | 180 | 181 | io.vertx 182 | vertx-service-discovery 183 | test-jar 184 | test 185 | 186 | 187 | org.assertj 188 | assertj-core 189 | 3.3.0 190 | test 191 | 192 | 193 | com.jayway.awaitility 194 | awaitility 195 | 1.7.0 196 | test 197 | 198 | 199 | ch.qos.logback 200 | logback-classic 201 | 1.3.12 202 | test 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-surefire-plugin 213 | 214 | false 215 | 216 | PARANOID 217 | ${project.build.directory} 218 | ${project.version} 219 | true 220 | io.vertx.core.logging.SLF4JLogDelegateFactory 221 | 222 | 1000 223 | 224 | 225 | -Xmx1200M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${project.build.directory} 226 | 1 227 | true 228 | 229 | **/it/**/*Test.java 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | maven-failsafe-plugin 238 | 239 | 240 | core 241 | 242 | integration-test 243 | verify 244 | 245 | integration-test 246 | 247 | 248 | PARANOID 249 | ${project.build.directory} 250 | ${project.version} 251 | true 252 | io.vertx.core.logging.SLF4JLogDelegateFactory 253 | 254 | 1000 255 | 256 | 257 | **/it/core/*Test.java 258 | 259 | 260 | io.vertx:vertx-core:test-jar 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | coverage 272 | 273 | 274 | 275 | 276 | org.apache.maven.plugins 277 | maven-surefire-plugin 278 | 279 | false 280 | 281 | PARANOID 282 | ${project.build.directory} 283 | ${project.version} 284 | true 285 | 286 | 287 | 288 | -Xmx1200M 289 | 1 290 | true 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | = Infinispan Cluster Manager 2 | 3 | This is a cluster manager implementation for Vert.x that uses https://infinispan.org/[Infinispan]. 4 | 5 | This implementation is packaged inside: 6 | 7 | [source,xml,subs="+attributes"] 8 | ---- 9 | 10 | ${maven.groupId} 11 | ${maven.artifactId} 12 | ${maven.version} 13 | 14 | ---- 15 | 16 | In Vert.x a cluster manager is used for various functions including: 17 | 18 | * Discovery and group membership of Vert.x nodes in a cluster 19 | * Maintaining cluster wide topic subscriber lists (so we know which nodes are interested in which event bus addresses) 20 | * Distributed Map support 21 | * Distributed Locks 22 | * Distributed Counters 23 | 24 | Cluster managers *do not* handle the event bus inter-node transport, this is done directly by Vert.x with TCP connections. 25 | 26 | == Using this cluster manager 27 | 28 | If you are using Vert.x from the command line, the jar corresponding to this cluster manager (it will be named `${maven.artifactId}-${maven.version}.jar` 29 | should be in the `lib` directory of the Vert.x installation. 30 | 31 | If you want clustering with this cluster manager in your Vert.x Maven or Gradle project then just add a dependency to 32 | the artifact: `${maven.groupId}:${maven.artifactId}:${maven.version}` in your project. 33 | 34 | If the jar is on your classpath as above then Vert.x will automatically detect this and use it as the cluster manager. 35 | Please make sure you don't have any other cluster managers on your classpath or Vert.x might 36 | choose the wrong one. 37 | 38 | You can also specify the cluster manager programmatically if you are embedding Vert.x by specifying it on the options 39 | when you are creating your Vert.x instance, for example: 40 | 41 | [source,$lang] 42 | ---- 43 | {@link examples.Examples#createClusterManagerProgramatically()} 44 | ---- 45 | 46 | == Configuring this cluster manager 47 | 48 | The default cluster manager configuration can be modified with `infinispan.xml` and/or `jgroups.xml` files. 49 | The former configures the data grid, the latter group management and member discovery. 50 | 51 | You can place one or both of them on your classpath. 52 | If you want to embed your custom file in a fat jar, it must be located at the root of the fat jar. 53 | If it's an external file, the **directory** containing the file must be added to the classpath. For 54 | example, if you are using the _launcher_ class from Vert.x, the classpath enhancement can be done as follows: 55 | 56 | [source,shell] 57 | ---- 58 | # If infinispan.xml and/or jgroups.xml files are in the current directory: 59 | java -jar my-app.jar -cp . -cluster 60 | 61 | # If infinispan.xml and/or jgroups.xml files are in the conf directory: 62 | java -jar my-app.jar -cp conf -cluster 63 | ---- 64 | 65 | Another way to override the configuration is by providing the file locations via system properties: 66 | `vertx.infinispan.config` and/or `vertx.jgroups.config`. 67 | 68 | [source,shell] 69 | ---- 70 | # Use a cluster configuration located in an external file 71 | java -Dvertx.infinispan.config=./config/my-infinispan.xml -jar ... -cluster 72 | 73 | # Or use a custom configuration from the classpath 74 | java -Dvertx.infinispan.config=my/package/config/my-infinispan.xml -jar ... -cluster 75 | ---- 76 | 77 | The cluster manager will search for the file in classpath first, and fallback to the filesystem. 78 | 79 | The system properties, when present, override any `infinispan.xml` or `jgroups.xml` on the classpath. 80 | 81 | The xml files are Infinispan and JGroups configuration files and are described in detail in the documentation on the Infinispan and JGroups web-sites. 82 | 83 | IMPORTANT: if a `jgroups.xml` file is on the classpath or if you set the `vertx.jgroups.config` system property, 84 | it will override any JGroups `stack-file` path defined in the Infinispan configuration file. 85 | 86 | The default JGroups configuration uses multicast for discovery and TCP for group management. 87 | Make sure multicast is enabled on your network for this to work. 88 | 89 | For full documentation on how to configure the transport differently or use a different transport please consult the 90 | Infinispan / JGroups documentations. 91 | 92 | == Using an existing Infinispan Cache Manager 93 | 94 | You can pass an existing `DefaultCacheManager` in the cluster manager to reuse an existing cache manager: 95 | 96 | [source,$lang] 97 | ---- 98 | {@link examples.Examples#useExistingCacheManager(org.infinispan.manager.DefaultCacheManager)} 99 | ---- 100 | 101 | In this case, vert.x is not the cache manager owner and so do not shut it down on close. 102 | 103 | Notice that the custom Infinispan instance need to be configured with: 104 | 105 | [source,xml] 106 | ---- 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ---- 115 | 116 | == Packaging an executable uber JAR 117 | 118 | Infinispan uses Java's `ServiceLoader` mechanism to discover implementations of a few classes at runtime. 119 | You must configure your build tool to merge service descriptors files when creating an executable uber JAR (also known as "fat" JAR). 120 | 121 | If you use Maven and the Maven Shade Plugin, the plugin configuration should look like: 122 | 123 | [source,xml] 124 | ---- 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | ---- 133 | 134 | If you use Gradle and the Gradle Shadow Plugin: 135 | 136 | [source,kotlin] 137 | ---- 138 | shadowJar { 139 | mergeServiceFiles() 140 | } 141 | ---- 142 | 143 | Besides, Infinispan relies on https://openjdk.org/jeps/238[Multi-Release JAR Files] to allow multiple, Java-release-specific versions of some classes. 144 | Consequently, the executable uber JAR's manifest file must contain the following entry: 145 | 146 | ---- 147 | Multi-Release: true 148 | ---- 149 | 150 | == Configuring for Kubernetes 151 | 152 | On Kubernetes, JGroups can be configured to use either the Kubernetes API (`KUBE_PING`) or DNS (`DNS_PING`) for discovery. 153 | In this document, we will use DNS discovery. 154 | 155 | First, force usage of IPv4 in the JVM with a system property. 156 | 157 | [source,shell] 158 | ---- 159 | -Djava.net.preferIPv4Stack=true 160 | ---- 161 | 162 | Then, set the `vertx.jgroups.config` system property to `default-configs/default-jgroups-kubernetes.xml`. 163 | This JGroups stack file is located in the `infinispan-core` JAR and preconfigured for Kubernetes. 164 | 165 | [source,shell] 166 | ---- 167 | -Dvertx.jgroups.config=default-configs/default-jgroups-kubernetes.xml 168 | ---- 169 | 170 | Also, set the JGroups DNS query to find members. 171 | 172 | [source,shell] 173 | ---- 174 | -Djgroups.dns.query=MY-SERVICE-DNS-NAME 175 | ---- 176 | 177 | The `MY-SERVICE-DNS-NAME` value must be a https://kubernetes.io/docs/user-guide/services/#headless-services[*headless* Kubernetes service] name that will be used by JGroups to identify all cluster members. 178 | A headless service can be created with: 179 | 180 | [source,yaml] 181 | ---- 182 | apiVersion: v1 183 | kind: Service 184 | metadata: 185 | name: clustered-app 186 | spec: 187 | selector: 188 | cluster: clustered-app # <2> 189 | ports: 190 | - name: jgroups 191 | port: 7800 # <1> 192 | protocol: TCP 193 | publishNotReadyAddresses: true # <3> 194 | clusterIP: None 195 | ---- 196 | <1> JGroups TCP port 197 | <2> Cluster members selected by the `cluster=clustered-app` label 198 | <3> Set to true so that members can be discovered without interfering with your readiness probe logic 199 | 200 | Eventually, apply the `cluster=clustered-app` label to all deployments that should be part of the cluster: 201 | 202 | [source,yaml] 203 | ---- 204 | apiVersion: apps/v1 205 | kind: Deployment 206 | spec: 207 | template: 208 | metadata: 209 | labels: 210 | cluster: clustered-app 211 | ---- 212 | 213 | === Rolling updates 214 | 215 | During rolling udpates, the Infinispan team http://infinispan.org/docs/stable/user_guide/user_guide.html#using_kubernetes_and_openshift_rolling_updates[recommends] to replace pods one by one. 216 | 217 | To do so, we must configure Kubernetes to: 218 | 219 | * never start more than one new pod at once 220 | * forbid more than one unavailable pod during the process 221 | 222 | [source,yaml] 223 | ---- 224 | spec: 225 | strategy: 226 | type: Rolling 227 | rollingParams: 228 | updatePeriodSeconds: 10 229 | intervalSeconds: 20 230 | timeoutSeconds: 600 231 | maxUnavailable: 1 <1> 232 | maxSurge: 1 <2> 233 | ---- 234 | <1> the maximum number of pods that can be unavailable during the update process 235 | <2> the maximum number of pods that can be created over the desired number of pods 236 | 237 | Also, pod readiness probe must take cluster health into account. 238 | Please refer to the <> section for details on how to implement a readiness probe with link:../../vertx-health-check/$lang/[Vert.x Health Checks]. 239 | 240 | == Configuring for Docker Compose 241 | 242 | Make sure to start the Java Virtual Machines with those system properties: 243 | 244 | [source,shell] 245 | ---- 246 | -Djava.net.preferIPv4Stack=true -Djgroups.bind.address=NON_LOOPBACK 247 | ---- 248 | 249 | This will make JGroups pick the interface of the virtual private network created by Docker. 250 | 251 | == Trouble shooting clustering 252 | 253 | If the default multicast discovery configuration is not working here are some common causes: 254 | 255 | === Multicast not enabled on the machine. 256 | 257 | It is quite common in particular on OSX machines for multicast to be disabled by default. Please google for 258 | information on how to enable this. 259 | 260 | === Using wrong network interface 261 | 262 | If you have more than one network interface on your machine (and this can also be the case if you are running 263 | VPN software on your machine), then JGroups may be using the wrong one. 264 | 265 | To tell JGroups to use a specific interface you can provide the IP address of the interface in the `bind_addr` 266 | element of the configuration. For example: 267 | 268 | [source,xml] 269 | ---- 270 | 273 | 276 | ---- 277 | 278 | Alternatively, if you want to stick with the bundled `jgroups.xml` file, you can set the `jgroups.bind.address` system property: 279 | 280 | [source,shell] 281 | ---- 282 | -Djgroups.bind.address=192.168.1.20 283 | ---- 284 | 285 | When running Vert.x is in clustered mode, you should also make sure that Vert.x knows about the correct interface. 286 | When running at the command line this is done by specifying the `cluster-host` option: 287 | 288 | [source,shell] 289 | ---- 290 | vertx run myverticle.js -cluster -cluster-host your-ip-address 291 | ---- 292 | 293 | Where `your-ip-address` is the same IP address you specified in the JGroups configuration. 294 | 295 | If using Vert.x programmatically you can specify this using {@link io.vertx.core.eventbus.EventBusOptions#setHost(java.lang.String)}. 296 | 297 | === Using a VPN 298 | 299 | This is a variation of the above case. 300 | VPN software often works by creating a virtual network interface which often doesn't support multicast. 301 | If you have a VPN running and you do not specify the correct interface to use in both the JGroups configuration and to Vert.x then the VPN interface may be chosen instead of the correct interface. 302 | 303 | So, if you have a VPN running you may have to configure both JGroups and Vert.x to use the correct interface as described in the previous section. 304 | 305 | === When multicast is not available 306 | 307 | In some cases you may not be able to use multicast discovery as it might not be available in your environment. 308 | In that case you should configure another protocol, e.g. `TCPPING` to use TCP sockets, or `S3_PING` when running on Amazon EC2. 309 | 310 | For more information on available JGroups discovery protocols and how to configure them please consult the http://www.jgroups.org/manual/index.html#Discovery[JGroups documentation]. 311 | 312 | === Problems with IPv6 313 | 314 | If you have troubles configuring an IPv6 host, force the use of IPv4 with the `java.net.preferIPv4Stack` system property. 315 | 316 | [source,shell] 317 | ---- 318 | -Djava.net.preferIPv4Stack=true 319 | ---- 320 | 321 | === Enabling logging 322 | 323 | When trouble-shooting clustering issues with it's often useful to get some logging output from Infinispan and JGroups 324 | to see if it's forming a cluster properly. You can do this (when using the default JUL logging) by adding a file 325 | called `vertx-default-jul-logging.properties` on your classpath. This is a standard java.util.logging (JUL) 326 | configuration file. Inside it set: 327 | 328 | [source,properties] 329 | ---- 330 | org.infinispan.level=INFO 331 | org.jgroups.level=INFO 332 | ---- 333 | 334 | and also 335 | 336 | [source,properties] 337 | ---- 338 | java.util.logging.ConsoleHandler.level=INFO 339 | java.util.logging.FileHandler.level=INFO 340 | ---- 341 | 342 | == Infinispan logging 343 | 344 | Infinispan relies on JBoss logging. JBoss Logging is a logging bridge providing integration with numerous logging frameworks. 345 | 346 | Add the logging JARs of you choice to the classpath and JBoss Logging will pick them up automatically. 347 | 348 | If you have multiple logging backends on your classpath, you can force selection with the `org.jboss.logging.provider` system property. 349 | For exeample: 350 | 351 | [source,shell] 352 | ---- 353 | -Dorg.jboss.logging.provider=log4j2 354 | ---- 355 | 356 | See this http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html[JBoss Logging guide] for more details. 357 | 358 | == JGroups logging 359 | 360 | JGroups uses JDK logging by default. log4j and log4j2 are supported if the corresponding JARs are found on the classpath. 361 | 362 | Please refer to the http://www.jgroups.org/manual/index.html#Logging[JGroups logging documentation] if you need 363 | more details or want to implement your own logging backend implementation. 364 | 365 | == SharedData extensions 366 | 367 | === AsyncMap content streams 368 | 369 | The `InfinispanAsyncMap` API allows to retrieve keys, values and entries as streams. 370 | This can be useful if you need to go through the content of a large map for bulk processing. 371 | 372 | [source,$lang] 373 | ---- 374 | {@link examples.Examples#asyncMapStreams(io.vertx.core.shareddata.AsyncMap)} 375 | ---- 376 | 377 | == Cluster administration 378 | 379 | The Infinispan cluster manager works by turning Vert.x nodes into members of an Infinispan cluster. 380 | As a consequence, Vert.x cluster manager administration should follow the Infinispan management guidelines. 381 | 382 | First, let's take a step back and introduce rebalancing and split-brain syndrome. 383 | 384 | === Rebalancing 385 | 386 | Each Vert.x node holds pieces of the clustering data: eventbus subscriptions, async map entries, clustered counters... etc. 387 | 388 | When a member joins or leaves the cluster, Infinispan rebalances cache entries on the new set of members. 389 | In other words, it moves data around to accomodate the new cluster topology. 390 | This process may take some time, depending on the amount of clustered data and number of nodes. 391 | 392 | === Split-brain syndrome 393 | 394 | In a perfect world, there would be no network equipment failures. 395 | Reality is, though, that sooner or later your cluster will be divided into smaller groups, unable to see each others. 396 | 397 | Infinispan is capable of merging the nodes back into a single cluster. 398 | But just as with rebalancing, this process may take some time. 399 | Before the cluster is fully functional again, some eventbus consumers might not be able to get messages. 400 | Or high-availability may not be able to redeploy a failing verticle. 401 | 402 | [NOTE] 403 | ==== 404 | It is difficult (if possible at all) to make a difference between a network partition and: 405 | 406 | - long GC pauses (leading to missed pings), 407 | - many nodes being killed forcefully, at-once, because you are deploying a new version of your application 408 | ==== 409 | 410 | === Recommendations 411 | 412 | Considering the common clustering issues discussed above, it is recommended to stick to the following good practices. 413 | 414 | ==== Graceful shutdown 415 | 416 | Avoid stopping members forcefully (e.g, `kill -9` a node). 417 | 418 | Of course process crashes are inevitable, but a graceful shutdown helps to get the remaining nodes in a stable state faster. 419 | 420 | [[one-by-one]] 421 | ==== One node after the other 422 | 423 | When rolling a new version of your app, scaling-up or down your cluster, add or remove nodes one after the other. 424 | 425 | Stopping nodes one by one prevents the cluster from thinking a network partition occured. 426 | Adding them one by one allows for clean, incremental rebalancing operations. 427 | 428 | The cluster healthiness can be verified with link:../../vertx-health-check/$lang/[Vert.x Health Checks]: 429 | 430 | [source,$lang] 431 | ---- 432 | {@link examples.Examples#healthCheck(io.vertx.core.Vertx)} 433 | ---- 434 | 435 | After creation, the health check can be exposed over HTTP with a link:../../vertx-web/$lang/[Vert.x Web] router handler: 436 | 437 | [source,$lang] 438 | ---- 439 | {@link examples.Examples#healthCheckHandler(io.vertx.core.Vertx, io.vertx.ext.healthchecks.HealthChecks)} 440 | ---- 441 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/examples/Examples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package examples; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.core.Promise; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.VertxOptions; 23 | import io.vertx.core.shareddata.AsyncMap; 24 | import io.vertx.core.spi.cluster.ClusterManager; 25 | import io.vertx.core.streams.ReadStream; 26 | import io.vertx.ext.cluster.infinispan.ClusterHealthCheck; 27 | import io.vertx.ext.cluster.infinispan.InfinispanAsyncMap; 28 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 29 | import io.vertx.ext.healthchecks.HealthChecks; 30 | import io.vertx.ext.healthchecks.Status; 31 | import io.vertx.ext.web.Router; 32 | import io.vertx.ext.web.healthchecks.HealthCheckHandler; 33 | import org.infinispan.manager.DefaultCacheManager; 34 | 35 | import java.util.Map; 36 | 37 | /** 38 | * @author Thomas Segismont 39 | */ 40 | public class Examples { 41 | 42 | public void createClusterManagerProgramatically() { 43 | ClusterManager mgr = new InfinispanClusterManager(); 44 | 45 | Vertx.builder() 46 | .withClusterManager(mgr) 47 | .buildClustered().onComplete(res -> { 48 | if (res.succeeded()) { 49 | Vertx vertx = res.result(); 50 | } else { 51 | // failed! 52 | } 53 | }); 54 | } 55 | 56 | public void useExistingCacheManager(DefaultCacheManager cacheManager) { 57 | ClusterManager mgr = new InfinispanClusterManager(cacheManager); 58 | 59 | Vertx.builder() 60 | .withClusterManager(mgr) 61 | .buildClustered().onComplete(res -> { 62 | if (res.succeeded()) { 63 | Vertx vertx = res.result(); 64 | } else { 65 | // failed! 66 | } 67 | }); 68 | } 69 | 70 | public void asyncMapStreams(AsyncMap asyncMap) { 71 | InfinispanAsyncMap infinispanAsyncMap = InfinispanAsyncMap.unwrap(asyncMap); 72 | ReadStream keyStream = infinispanAsyncMap.keyStream(); 73 | ReadStream valueStream = infinispanAsyncMap.valueStream(); 74 | ReadStream> entryReadStream = infinispanAsyncMap.entryStream(); 75 | } 76 | 77 | public void healthCheck(Vertx vertx) { 78 | Handler> procedure = ClusterHealthCheck.createProcedure(vertx, true); 79 | HealthChecks checks = HealthChecks.create(vertx).register("cluster-health", procedure); 80 | } 81 | 82 | public void healthCheckHandler(Vertx vertx, HealthChecks checks) { 83 | Router router = Router.router(vertx); 84 | router.get("/readiness").handler(HealthCheckHandler.createWithHealthChecks(checks)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/examples/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * @author Thomas Segismont 19 | */ 20 | @Source 21 | package examples; 22 | 23 | import io.vertx.docgen.Source; 24 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/ClusterHealthCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan; 18 | 19 | import io.vertx.codegen.annotations.VertxGen; 20 | import io.vertx.core.Handler; 21 | import io.vertx.core.Promise; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.ext.cluster.infinispan.impl.ClusterHealthCheckImpl; 24 | import io.vertx.ext.healthchecks.Status; 25 | 26 | /** 27 | * A helper to create Vert.x cluster {@link io.vertx.ext.healthchecks.HealthChecks} procedures. 28 | * 29 | * @author Thomas Segismont 30 | */ 31 | @VertxGen 32 | public interface ClusterHealthCheck extends Handler> { 33 | 34 | /** 35 | * Like {@link #createProcedure(Vertx, boolean)} with {@code details} set to {@code true}. 36 | */ 37 | static ClusterHealthCheck createProcedure(Vertx vertx) { 38 | return createProcedure(vertx, true); 39 | } 40 | 41 | /** 42 | * Creates a ready-to-use Vert.x cluster {@link io.vertx.ext.healthchecks.HealthChecks} procedure. 43 | * 44 | * @param vertx the instance of Vert.x, must not be {@code null} 45 | * @param detailed when set to {@code true}, {@link Status} data will be filled with cluster health details 46 | * @return a Vert.x cluster {@link io.vertx.ext.healthchecks.HealthChecks} procedure 47 | */ 48 | static ClusterHealthCheck createProcedure(Vertx vertx, boolean detailed) { 49 | return new ClusterHealthCheckImpl(vertx, detailed); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/InfinispanAsyncMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan; 18 | 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.shareddata.AsyncMap; 23 | import io.vertx.core.shareddata.impl.SharedDataImpl.WrappedAsyncMap; 24 | import io.vertx.core.streams.ReadStream; 25 | 26 | import java.util.Map; 27 | 28 | /** 29 | * Extensions to the generic {@link AsyncMap}. 30 | * 31 | * @author Thomas Segismont 32 | */ 33 | @VertxGen 34 | public interface InfinispanAsyncMap { 35 | 36 | /** 37 | * Unwraps a generic {@link AsyncMap} to an {@link InfinispanAsyncMap}. 38 | * 39 | * @throws IllegalArgumentException if underlying implementation is not Infinispan 40 | */ 41 | @SuppressWarnings("unchecked") 42 | static InfinispanAsyncMap unwrap(AsyncMap asyncMap) { 43 | if (asyncMap instanceof WrappedAsyncMap) { 44 | WrappedAsyncMap wrappedAsyncMap = (WrappedAsyncMap) asyncMap; 45 | AsyncMap delegate = wrappedAsyncMap.getDelegate(); 46 | if (delegate instanceof InfinispanAsyncMap) { 47 | return (InfinispanAsyncMap) delegate; 48 | } 49 | } 50 | throw new IllegalArgumentException(String.valueOf(asyncMap != null ? asyncMap.getClass() : null)); 51 | } 52 | 53 | /** 54 | * Get the keys of the map as a {@link ReadStream}. 55 | *

56 | * The stream will be automatically closed if it fails or ends. 57 | * Otherwise you must set a null {@link ReadStream#handler(Handler) data handler} after usage to avoid leaking resources. 58 | * 59 | * @return a stream of map keys 60 | */ 61 | ReadStream keyStream(); 62 | 63 | /** 64 | * Get the values of the map as a {@link ReadStream}. 65 | *

66 | * The stream will be automatically closed if it fails or ends. 67 | * Otherwise you must set a null {@link ReadStream#handler(Handler) data handler} after usage to avoid leaking resources. 68 | * 69 | * @return a stream of map values 70 | */ 71 | ReadStream valueStream(); 72 | 73 | /** 74 | * Get the entries of the map as a {@link ReadStream}. 75 | *

76 | * The stream will be automatically closed if it fails or ends. 77 | * Otherwise you must set a null {@link ReadStream#handler(Handler) data handler} after usage to avoid leaking resources. 78 | * 79 | * @return a stream of map entries 80 | */ 81 | @GenIgnore 82 | ReadStream> entryStream(); 83 | } 84 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/InfinispanClusterManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan; 18 | 19 | import io.vertx.core.Completable; 20 | import io.vertx.core.Future; 21 | import io.vertx.core.Promise; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.core.internal.VertxInternal; 24 | import io.vertx.core.internal.PromiseInternal; 25 | import io.vertx.core.internal.logging.Logger; 26 | import io.vertx.core.internal.logging.LoggerFactory; 27 | import io.vertx.core.shareddata.AsyncMap; 28 | import io.vertx.core.shareddata.Counter; 29 | import io.vertx.core.shareddata.Lock; 30 | import io.vertx.core.spi.cluster.*; 31 | import io.vertx.ext.cluster.infinispan.impl.*; 32 | import org.infinispan.AdvancedCache; 33 | import org.infinispan.Cache; 34 | import org.infinispan.commons.api.BasicCacheContainer; 35 | import org.infinispan.commons.api.CacheContainerAdmin; 36 | import org.infinispan.commons.util.FileLookup; 37 | import org.infinispan.commons.util.FileLookupFactory; 38 | import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; 39 | import org.infinispan.configuration.parsing.ParserRegistry; 40 | import org.infinispan.context.Flag; 41 | import org.infinispan.counter.EmbeddedCounterManagerFactory; 42 | import org.infinispan.counter.api.CounterConfiguration; 43 | import org.infinispan.counter.api.CounterManager; 44 | import org.infinispan.counter.api.CounterType; 45 | import org.infinispan.lock.EmbeddedClusteredLockManagerFactory; 46 | import org.infinispan.lock.api.ClusteredLock; 47 | import org.infinispan.lock.impl.manager.EmbeddedClusteredLockManager; 48 | import org.infinispan.manager.DefaultCacheManager; 49 | import org.infinispan.manager.EmbeddedCacheManagerAdmin; 50 | import org.infinispan.notifications.Listener; 51 | import org.infinispan.notifications.cachemanagerlistener.annotation.Merged; 52 | import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged; 53 | import org.infinispan.notifications.cachemanagerlistener.event.MergeEvent; 54 | import org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent; 55 | import org.infinispan.remoting.transport.Address; 56 | import org.infinispan.remoting.transport.jgroups.JGroupsTransport; 57 | import org.jgroups.stack.Protocol; 58 | 59 | import java.net.InetAddress; 60 | import java.net.URL; 61 | import java.util.ArrayList; 62 | import java.util.List; 63 | import java.util.Map; 64 | import java.util.Objects; 65 | import java.util.concurrent.TimeUnit; 66 | 67 | import static java.util.stream.Collectors.toList; 68 | 69 | /** 70 | * @author Thomas Segismont 71 | */ 72 | public class InfinispanClusterManager implements ClusterManager { 73 | 74 | private static final Logger log = LoggerFactory.getLogger(InfinispanClusterManager.class); 75 | 76 | private static final String VERTX_INFINISPAN_CONFIG_PROP_NAME = "vertx.infinispan.config"; 77 | private static final String INFINISPAN_XML = "infinispan.xml"; 78 | private static final String DEFAULT_INFINISPAN_XML = "default-infinispan.xml"; 79 | private static final String VERTX_JGROUPS_CONFIG_PROP_NAME = "vertx.jgroups.config"; 80 | private static final String JGROUPS_XML = "jgroups.xml"; 81 | 82 | private final String ispnConfigPath; 83 | private final String jgroupsConfigPath; 84 | private final boolean userProvidedCacheManager; 85 | 86 | private VertxInternal vertx; 87 | private DefaultCacheManager cacheManager; 88 | private RegistrationListener registrationListener; 89 | private NodeListener nodeListener; 90 | private EmbeddedClusteredLockManager lockManager; 91 | private CounterManager counterManager; 92 | private NodeInfo nodeInfo; 93 | private AdvancedCache nodeInfoCache; 94 | private SubsCacheHelper subsCacheHelper; 95 | private volatile boolean active; 96 | private ClusterViewListener viewListener; 97 | 98 | /** 99 | * Creates a new cluster manager configured with {@code infinispan.xml} and {@code jgroups.xml} files. 100 | */ 101 | public InfinispanClusterManager() { 102 | this.ispnConfigPath = System.getProperty(VERTX_INFINISPAN_CONFIG_PROP_NAME, INFINISPAN_XML); 103 | this.jgroupsConfigPath = System.getProperty(VERTX_JGROUPS_CONFIG_PROP_NAME, JGROUPS_XML); 104 | userProvidedCacheManager = false; 105 | } 106 | 107 | /** 108 | * Creates a new cluster manager with an existing {@link DefaultCacheManager}. 109 | * It is your responsibility to start/stop the cache manager when the Vert.x instance joins/leaves the cluster. 110 | * 111 | * @param cacheManager the existing cache manager 112 | */ 113 | public InfinispanClusterManager(DefaultCacheManager cacheManager) { 114 | Objects.requireNonNull(cacheManager, "cacheManager"); 115 | this.cacheManager = cacheManager; 116 | ispnConfigPath = null; 117 | jgroupsConfigPath = null; 118 | userProvidedCacheManager = true; 119 | } 120 | 121 | @Override 122 | public void init(Vertx vertx) { 123 | this.vertx = (VertxInternal) vertx; 124 | this.registrationListener = registrationListener; 125 | } 126 | 127 | public BasicCacheContainer getCacheContainer() { 128 | return cacheManager; 129 | } 130 | 131 | @Override 132 | public void getAsyncMap(String name, Completable> promise) { 133 | vertx.>executeBlocking(() -> { 134 | EmbeddedCacheManagerAdmin administration = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE); 135 | Cache cache = administration.getOrCreateCache(name, "__vertx.distributed.cache.configuration"); 136 | return new InfinispanAsyncMapImpl<>(vertx, cache); 137 | }, false).onComplete(promise); 138 | } 139 | 140 | @Override 141 | public Map getSyncMap(String name) { 142 | return cacheManager.getCache(name); 143 | } 144 | 145 | @Override 146 | public void getLockWithTimeout(String name, long timeout, Completable prom) { 147 | vertx.executeBlocking(() -> { 148 | PromiseInternal promise = vertx.promise(); 149 | if (!lockManager.isDefined(name)) { 150 | lockManager.defineLock(name); 151 | } 152 | ClusteredLock lock = lockManager.get(name); 153 | lock.tryLock(timeout, TimeUnit.MILLISECONDS).whenComplete((locked, throwable) -> { 154 | if (throwable == null) { 155 | if (locked) { 156 | promise.complete(new InfinispanLock(lock)); 157 | } else { 158 | promise.fail("Timed out waiting to get lock " + name); 159 | } 160 | } else { 161 | promise.fail(throwable); 162 | } 163 | }); 164 | return promise.future(); 165 | }, false).compose(f -> f).onComplete(prom); 166 | } 167 | 168 | @Override 169 | public void getCounter(String name, Completable promise) { 170 | vertx.executeBlocking(() -> { 171 | if (!counterManager.isDefined(name)) { 172 | counterManager.defineCounter(name, CounterConfiguration.builder(CounterType.UNBOUNDED_STRONG).build()); 173 | } 174 | return new InfinispanCounter(vertx, counterManager.getStrongCounter(name).sync()); 175 | }, false).onComplete(promise); 176 | } 177 | 178 | @Override 179 | public String getNodeId() { 180 | return cacheManager.getNodeAddress(); 181 | } 182 | 183 | @Override 184 | public List getNodes() { 185 | return cacheManager.getTransport().getMembers().stream().map(Address::toString).collect(toList()); 186 | } 187 | 188 | @Override 189 | public synchronized void registrationListener(RegistrationListener registrationListener) { 190 | this.registrationListener = registrationListener; 191 | } 192 | 193 | @Override 194 | public synchronized void nodeListener(NodeListener nodeListener) { 195 | this.nodeListener = nodeListener; 196 | } 197 | 198 | @Override 199 | public void setNodeInfo(NodeInfo nodeInfo, Completable promise) { 200 | synchronized (this) { 201 | this.nodeInfo = nodeInfo; 202 | } 203 | byte[] value = DataConverter.toCachedObject(nodeInfo); 204 | Future.fromCompletionStage(nodeInfoCache.withFlags(Flag.IGNORE_RETURN_VALUES).putAsync(getNodeId(), value)) 205 | .mapEmpty() 206 | .onComplete(promise); 207 | } 208 | 209 | @Override 210 | public synchronized NodeInfo getNodeInfo() { 211 | return nodeInfo; 212 | } 213 | 214 | @Override 215 | public void getNodeInfo(String nodeId, Completable promise) { 216 | nodeInfoCache.getAsync(nodeId).whenComplete((nodeInfo, throwable) -> { 217 | if (throwable != null) { 218 | promise.fail(throwable); 219 | } else if (nodeInfo == null) { 220 | promise.fail("Not a member of the cluster"); 221 | } else { 222 | promise.succeed(DataConverter.fromCachedObject(nodeInfo)); 223 | } 224 | }); 225 | } 226 | 227 | @Override 228 | public void join(Completable promise) { 229 | vertx.executeBlocking(() -> { 230 | if (active) { 231 | return null; 232 | } 233 | active = true; 234 | if (!userProvidedCacheManager) { 235 | FileLookup fileLookup = FileLookupFactory.newInstance(); 236 | 237 | URL ispnConfig = fileLookup.lookupFileLocation(ispnConfigPath, getCTCCL()); 238 | if (ispnConfig == null) { 239 | log.warn("Cannot find Infinispan config '" + ispnConfigPath + "', using default"); 240 | ispnConfig = fileLookup.lookupFileLocation(DEFAULT_INFINISPAN_XML, getCTCCL()); 241 | } 242 | ConfigurationBuilderHolder builderHolder = new ParserRegistry().parse(ispnConfig); 243 | // Workaround Launcher in fatjar issue (context classloader may be null) 244 | ClassLoader classLoader = getCTCCL(); 245 | if (classLoader == null) { 246 | classLoader = getClass().getClassLoader(); 247 | } 248 | builderHolder.getGlobalConfigurationBuilder().classLoader(classLoader); 249 | 250 | if (fileLookup.lookupFileLocation(jgroupsConfigPath, getCTCCL()) != null) { 251 | log.warn("Forcing JGroups config to '" + jgroupsConfigPath + "'"); 252 | builderHolder.getGlobalConfigurationBuilder().transport().defaultTransport() 253 | .removeProperty(JGroupsTransport.CHANNEL_CONFIGURATOR) 254 | .addProperty(JGroupsTransport.CONFIGURATION_FILE, jgroupsConfigPath); 255 | } 256 | 257 | cacheManager = new DefaultCacheManager(builderHolder, true); 258 | } 259 | viewListener = new ClusterViewListener(); 260 | cacheManager.addListener(viewListener); 261 | 262 | subsCacheHelper = new SubsCacheHelper(vertx, cacheManager, registrationListener); 263 | 264 | nodeInfoCache = cacheManager.getCache("__vertx.nodeInfo").getAdvancedCache(); 265 | 266 | lockManager = (EmbeddedClusteredLockManager) EmbeddedClusteredLockManagerFactory.from(cacheManager); 267 | counterManager = EmbeddedCounterManagerFactory.asCounterManager(cacheManager); 268 | 269 | return null; 270 | }, false).onComplete(promise); 271 | } 272 | 273 | private ClassLoader getCTCCL() { 274 | return Thread.currentThread().getContextClassLoader(); 275 | } 276 | 277 | @Override 278 | public void leave(Completable promise) { 279 | vertx.executeBlocking(() -> { 280 | if (!active) { 281 | return null; 282 | } 283 | active = false; 284 | subsCacheHelper.close(); 285 | cacheManager.removeListener(viewListener); 286 | if (!userProvidedCacheManager) { 287 | cacheManager.stop(); 288 | } 289 | return null; 290 | }, false).onComplete(promise); 291 | } 292 | 293 | @Override 294 | public boolean isActive() { 295 | return active; 296 | } 297 | 298 | @Override 299 | public void addRegistration(String address, RegistrationInfo registrationInfo, Completable promise) { 300 | SubsOpSerializer serializer = SubsOpSerializer.get(vertx.getOrCreateContext()); 301 | serializer.execute(subsCacheHelper::put, address, registrationInfo, promise); 302 | } 303 | 304 | @Override 305 | public void removeRegistration(String address, RegistrationInfo registrationInfo, Completable promise) { 306 | SubsOpSerializer serializer = SubsOpSerializer.get(vertx.getOrCreateContext()); 307 | serializer.execute(subsCacheHelper::remove, address, registrationInfo, promise); 308 | } 309 | 310 | @Override 311 | public void getRegistrations(String address, Completable> promise) { 312 | Future.fromCompletionStage(subsCacheHelper.get(address)).onComplete(promise); 313 | } 314 | 315 | @Override 316 | public String clusterHost() { 317 | return getHostFromTransportProtocol("bind_addr"); 318 | } 319 | 320 | @Override 321 | public String clusterPublicHost() { 322 | return getHostFromTransportProtocol("external_addr"); 323 | } 324 | 325 | private String getHostFromTransportProtocol(String fieldName) { 326 | JGroupsTransport transport = (JGroupsTransport) cacheManager.getTransport(); 327 | Protocol bottomProtocol = transport.getChannel().getProtocolStack().getBottomProtocol(); 328 | try { 329 | InetAddress external_addr = (InetAddress) bottomProtocol.getValue(fieldName); 330 | String str = external_addr.toString(); 331 | if (str.charAt(0) == '/') { 332 | return str.substring(1); 333 | } 334 | return str.substring(0, str.indexOf('/')); 335 | } catch (Exception ignored) { 336 | return null; 337 | } 338 | } 339 | 340 | @Listener(sync = false) 341 | private class ClusterViewListener { 342 | @ViewChanged 343 | public void handleViewChange(ViewChangedEvent e) { 344 | handleViewChangeInternal(e); 345 | } 346 | 347 | @Merged 348 | public void handleMerge(MergeEvent e) { 349 | handleViewChangeInternal(e); 350 | } 351 | 352 | private void handleViewChangeInternal(ViewChangedEvent e) { 353 | synchronized (InfinispanClusterManager.this) { 354 | if (!active) { 355 | return; 356 | } 357 | 358 | List

added = new ArrayList<>(e.getNewMembers()); 359 | added.removeAll(e.getOldMembers()); 360 | if (log.isDebugEnabled()) { 361 | log.debug("Members added = " + added); 362 | } 363 | added.forEach(address -> { 364 | if (nodeListener != null) { 365 | nodeListener.nodeAdded(address.toString()); 366 | } 367 | }); 368 | 369 | List
removed = new ArrayList<>(e.getOldMembers()); 370 | removed.removeAll(e.getNewMembers()); 371 | if (log.isDebugEnabled()) { 372 | log.debug("Members removed = " + removed); 373 | } 374 | if (isMaster()) { 375 | cleanSubs(removed); 376 | cleanNodeInfos(removed); 377 | } 378 | removed.forEach(address -> { 379 | if (nodeListener != null) { 380 | nodeListener.nodeLeft(address.toString()); 381 | } 382 | }); 383 | } 384 | } 385 | } 386 | 387 | private boolean isMaster() { 388 | return cacheManager.isCoordinator(); 389 | } 390 | 391 | private void cleanSubs(List
removed) { 392 | removed.stream().map(Address::toString).forEach(subsCacheHelper::removeAllForNode); 393 | } 394 | 395 | private void cleanNodeInfos(List
removed) { 396 | removed.stream().map(Address::toString).forEach(nodeInfoCache::remove); 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/CloseableIteratorCollectionStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.Context; 20 | import io.vertx.core.Handler; 21 | import io.vertx.core.streams.ReadStream; 22 | import org.infinispan.commons.util.CloseableIterator; 23 | import org.infinispan.commons.util.CloseableIteratorCollection; 24 | 25 | import java.util.ArrayDeque; 26 | import java.util.ArrayList; 27 | import java.util.Deque; 28 | import java.util.List; 29 | import java.util.concurrent.atomic.AtomicReference; 30 | import java.util.function.Function; 31 | import java.util.function.Supplier; 32 | 33 | /** 34 | * @author Thomas Segismont 35 | */ 36 | public class CloseableIteratorCollectionStream implements ReadStream { 37 | 38 | private static final int BATCH_SIZE = 10; 39 | 40 | private final Context context; 41 | private final Supplier> iterableSupplier; 42 | private final Function converter; 43 | 44 | private CloseableIteratorCollection iterable; 45 | private CloseableIterator iterator; 46 | private Deque queue; 47 | private Handler dataHandler; 48 | private Handler exceptionHandler; 49 | private Handler endHandler; 50 | private long demand = Long.MAX_VALUE; 51 | private boolean readInProgress; 52 | private boolean closed; 53 | 54 | public CloseableIteratorCollectionStream(Context context, Supplier> iterableSupplier, Function converter) { 55 | this.context = context; 56 | this.iterableSupplier = iterableSupplier; 57 | this.converter = converter; 58 | } 59 | 60 | @Override 61 | public synchronized CloseableIteratorCollectionStream exceptionHandler(Handler handler) { 62 | checkClosed(); 63 | this.exceptionHandler = handler; 64 | return this; 65 | } 66 | 67 | private void checkClosed() { 68 | if (closed) { 69 | throw new IllegalArgumentException("Stream is closed"); 70 | } 71 | } 72 | 73 | @Override 74 | public synchronized CloseableIteratorCollectionStream handler(Handler handler) { 75 | checkClosed(); 76 | if (handler == null) { 77 | close(); 78 | } else { 79 | dataHandler = handler; 80 | context.>executeBlocking(iterableSupplier::get, false).onComplete(ar -> { 81 | synchronized (this) { 82 | if (ar.succeeded()) { 83 | iterable = ar.result(); 84 | if (canRead()) { 85 | doRead(); 86 | } 87 | } else { 88 | close(); 89 | handleException(ar.cause()); 90 | } 91 | } 92 | }); 93 | } 94 | return this; 95 | } 96 | 97 | private boolean canRead() { 98 | return demand > 0L && !closed; 99 | } 100 | 101 | @Override 102 | public synchronized CloseableIteratorCollectionStream pause() { 103 | checkClosed(); 104 | demand = 0L; 105 | return this; 106 | } 107 | 108 | @Override 109 | public CloseableIteratorCollectionStream fetch(long amount) { 110 | checkClosed(); 111 | if (amount > 0L) { 112 | demand += amount; 113 | if (demand < 0L) { 114 | demand = Long.MAX_VALUE; 115 | } 116 | if (dataHandler != null && iterable != null) { 117 | doRead(); 118 | } 119 | } 120 | return this; 121 | } 122 | 123 | @Override 124 | public synchronized CloseableIteratorCollectionStream resume() { 125 | return fetch(Long.MAX_VALUE); 126 | } 127 | 128 | private synchronized void doRead() { 129 | if (readInProgress) { 130 | return; 131 | } 132 | readInProgress = true; 133 | if (iterator == null) { 134 | context.>executeBlocking(() -> iterable.iterator(), false).onComplete(ar -> { 135 | synchronized (this) { 136 | readInProgress = false; 137 | if (ar.succeeded()) { 138 | iterator = ar.result(); 139 | if (canRead()) { 140 | doRead(); 141 | } 142 | } else { 143 | close(); 144 | handleException(ar.cause()); 145 | } 146 | } 147 | }); 148 | return; 149 | } 150 | if (queue == null) { 151 | queue = new ArrayDeque<>(BATCH_SIZE); 152 | } 153 | if (!queue.isEmpty()) { 154 | context.runOnContext(v -> emitQueued()); 155 | return; 156 | } 157 | context.>executeBlocking(() -> { 158 | List batch = new ArrayList<>(BATCH_SIZE); 159 | for (int i = 0; i < BATCH_SIZE && iterator.hasNext(); i++) { 160 | batch.add(iterator.next()); 161 | } 162 | return batch; 163 | }, false).onComplete(ar -> { 164 | synchronized (this) { 165 | if (ar.succeeded()) { 166 | queue.addAll(ar.result()); 167 | if (queue.isEmpty()) { 168 | close(); 169 | if (endHandler != null) { 170 | endHandler.handle(null); 171 | } 172 | } else { 173 | emitQueued(); 174 | } 175 | } else { 176 | close(); 177 | handleException(ar.cause()); 178 | } 179 | } 180 | }); 181 | } 182 | 183 | private void handleException(Throwable cause) { 184 | if (exceptionHandler != null) { 185 | exceptionHandler.handle(cause); 186 | } 187 | } 188 | 189 | private synchronized void emitQueued() { 190 | while (!queue.isEmpty() && canRead()) { 191 | if (demand != Long.MAX_VALUE) { 192 | demand--; 193 | } 194 | dataHandler.handle(converter.apply(queue.remove())); 195 | } 196 | readInProgress = false; 197 | if (canRead()) { 198 | doRead(); 199 | } 200 | } 201 | 202 | @Override 203 | public synchronized CloseableIteratorCollectionStream endHandler(Handler handler) { 204 | endHandler = handler; 205 | return this; 206 | } 207 | 208 | private void close() { 209 | closed = true; 210 | AtomicReference> iteratorRef = new AtomicReference<>(); 211 | context.executeBlocking(() -> { 212 | synchronized (this) { 213 | iteratorRef.set(iterator); 214 | } 215 | CloseableIterator iter = iteratorRef.get(); 216 | if (iter != null) { 217 | iter.close(); 218 | } 219 | return null; 220 | }, false); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/ClusterHealthCheckImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.Promise; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.internal.VertxInternal; 22 | import io.vertx.core.json.JsonArray; 23 | import io.vertx.core.json.JsonObject; 24 | import io.vertx.ext.cluster.infinispan.ClusterHealthCheck; 25 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 26 | import io.vertx.ext.healthchecks.Status; 27 | import org.infinispan.health.CacheHealth; 28 | import org.infinispan.health.ClusterHealth; 29 | import org.infinispan.health.Health; 30 | import org.infinispan.health.HealthStatus; 31 | import org.infinispan.manager.EmbeddedCacheManager; 32 | 33 | import java.util.List; 34 | 35 | /** 36 | * @author Thomas Segismont 37 | */ 38 | public class ClusterHealthCheckImpl implements ClusterHealthCheck { 39 | 40 | private final Vertx vertx; 41 | private final boolean detailed; 42 | 43 | public ClusterHealthCheckImpl(Vertx vertx, boolean detailed) { 44 | this.vertx = vertx; 45 | this.detailed = detailed; 46 | } 47 | 48 | @Override 49 | public void handle(Promise promise) { 50 | VertxInternal vertxInternal = (VertxInternal) vertx; 51 | InfinispanClusterManager clusterManager = (InfinispanClusterManager) vertxInternal.clusterManager(); 52 | EmbeddedCacheManager cacheManager = (EmbeddedCacheManager) clusterManager.getCacheContainer(); 53 | Health health = cacheManager.getHealth(); 54 | HealthStatus healthStatus = health.getClusterHealth().getHealthStatus(); 55 | Status status = new Status().setOk(healthStatus == HealthStatus.HEALTHY); 56 | if (detailed) { 57 | status.setData(convert(health)); 58 | } 59 | promise.complete(status); 60 | } 61 | 62 | private JsonObject convert(Health health) { 63 | return new JsonObject() 64 | .put("clusterHealth", convert(health.getClusterHealth())) 65 | .put("cacheHealth", convert(health.getCacheHealth())); 66 | } 67 | 68 | private JsonObject convert(ClusterHealth clusterHealth) { 69 | return new JsonObject() 70 | .put("healthStatus", clusterHealth.getHealthStatus().name()) 71 | .put("clusterName", clusterHealth.getClusterName()) 72 | .put("numberOfNodes", clusterHealth.getNumberOfNodes()) 73 | .put("nodeNames", clusterHealth.getNodeNames().stream() 74 | .collect(JsonArray::new, JsonArray::add, JsonArray::addAll)); 75 | } 76 | 77 | private JsonArray convert(List cacheHealths) { 78 | return cacheHealths.stream() 79 | .map(cacheHealth -> new JsonObject() 80 | .put("cacheName", cacheHealth.getCacheName()) 81 | .put("status", cacheHealth.getStatus().name())) 82 | .collect(JsonArray::new, JsonArray::add, JsonArray::addAll); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/DataConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.buffer.Buffer; 20 | import io.vertx.core.shareddata.ClusterSerializable; 21 | 22 | import java.io.*; 23 | import java.nio.charset.StandardCharsets; 24 | 25 | /** 26 | * @author Thomas Segismont 27 | */ 28 | public class DataConverter { 29 | 30 | public static byte[] toCachedObject(Object o) { 31 | if (o == null) { 32 | return null; 33 | } 34 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 35 | if (o instanceof Serializable) { 36 | baos.write(0); 37 | writeSerializable(baos, (Serializable) o); 38 | } else if (o instanceof ClusterSerializable) { 39 | baos.write(1); 40 | writeClusterSerializable(baos, (ClusterSerializable) o); 41 | } else { 42 | throw new IllegalArgumentException("Cannot convert object of type: " + o.getClass()); 43 | } 44 | return baos.toByteArray(); 45 | } 46 | 47 | private static void writeSerializable(ByteArrayOutputStream baos, Serializable o) { 48 | try (ObjectOutputStream out = new ObjectOutputStream(baos)) { 49 | out.writeObject(o); 50 | } catch (IOException e) { 51 | throw new RuntimeException(e); 52 | } 53 | } 54 | 55 | private static void writeClusterSerializable(ByteArrayOutputStream baos, ClusterSerializable o) { 56 | Buffer buffer = Buffer.buffer(); 57 | byte[] classNameBytes = o.getClass().getName().getBytes(StandardCharsets.UTF_8); 58 | buffer.appendInt(classNameBytes.length).appendBytes(classNameBytes); 59 | o.writeToBuffer(buffer); 60 | baos.write(buffer.getBytes(), 0, buffer.length()); 61 | } 62 | 63 | @SuppressWarnings("unchecked") 64 | public static T fromCachedObject(byte[] value) { 65 | if (value == null) { 66 | return null; 67 | } 68 | byte type = value[0]; 69 | if (type == 0) { 70 | return (T) readSerializable(value); 71 | } else if (type == 1) { 72 | return (T) readClusterSerializable(value); 73 | } 74 | throw new IllegalArgumentException("Cannot convert object of type: " + type); 75 | } 76 | 77 | private static Object readSerializable(byte[] value) { 78 | ByteArrayInputStream bais = new ByteArrayInputStream(value, 1, value.length - 1); 79 | try (ObjectInputStream in = new ObjectInputStream(bais)) { 80 | return in.readObject(); 81 | } catch (IOException | ClassNotFoundException e) { 82 | throw new RuntimeException(e); 83 | } 84 | } 85 | 86 | private static ClusterSerializable readClusterSerializable(byte[] value) { 87 | Buffer buffer = Buffer.buffer(value); 88 | int pos = 1, len; 89 | len = buffer.getInt(pos); 90 | pos += 4; 91 | byte[] classNameBytes = buffer.getBytes(pos, pos + len); 92 | pos += len; 93 | ClusterSerializable clusterSerializable; 94 | try { 95 | Class clazz = getClassLoader().loadClass(new String(classNameBytes, StandardCharsets.UTF_8)); 96 | clusterSerializable = (ClusterSerializable) clazz.newInstance(); 97 | } catch (Exception e) { 98 | throw new RuntimeException(e); 99 | } 100 | clusterSerializable.readFromBuffer(pos, buffer); 101 | return clusterSerializable; 102 | } 103 | 104 | private static ClassLoader getClassLoader() { 105 | ClassLoader tccl = Thread.currentThread().getContextClassLoader(); 106 | return tccl != null ? tccl:DataConverter.class.getClassLoader(); 107 | } 108 | 109 | private DataConverter() { 110 | // Utility class 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/InfinispanAsyncMapImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.internal.VertxInternal; 21 | import io.vertx.core.shareddata.AsyncMap; 22 | import io.vertx.core.streams.ReadStream; 23 | import io.vertx.ext.cluster.infinispan.InfinispanAsyncMap; 24 | import org.infinispan.AdvancedCache; 25 | import org.infinispan.Cache; 26 | import org.infinispan.context.Flag; 27 | import org.infinispan.stream.CacheCollectors; 28 | 29 | import java.util.AbstractMap.SimpleImmutableEntry; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Map.Entry; 34 | import java.util.Set; 35 | import java.util.concurrent.CompletableFuture; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.stream.Collectors; 38 | 39 | import static java.util.stream.Collectors.*; 40 | 41 | /** 42 | * @author Thomas Segismont 43 | */ 44 | public class InfinispanAsyncMapImpl implements AsyncMap, InfinispanAsyncMap { 45 | 46 | private final VertxInternal vertx; 47 | private final AdvancedCache cache; 48 | private final AdvancedCache ignoreReturnCache; 49 | 50 | public InfinispanAsyncMapImpl(VertxInternal vertx, Cache cache) { 51 | this.vertx = vertx; 52 | this.cache = cache.getAdvancedCache(); 53 | ignoreReturnCache = this.cache.withFlags(Flag.IGNORE_RETURN_VALUES); 54 | } 55 | 56 | @Override 57 | public Future get(K k) { 58 | byte[] kk = DataConverter.toCachedObject(k); 59 | return Future.fromCompletionStage(cache.getAsync(kk), vertx.getOrCreateContext()) 60 | .map(DataConverter::fromCachedObject); 61 | } 62 | 63 | @Override 64 | public Future put(K k, V v) { 65 | byte[] kk = DataConverter.toCachedObject(k); 66 | byte[] vv = DataConverter.toCachedObject(v); 67 | return Future.fromCompletionStage(ignoreReturnCache.putAsync(kk, vv), vertx.getOrCreateContext()).mapEmpty(); 68 | } 69 | 70 | @Override 71 | public Future put(K k, V v, long ttl) { 72 | byte[] kk = DataConverter.toCachedObject(k); 73 | byte[] vv = DataConverter.toCachedObject(v); 74 | CompletableFuture completionStage = ignoreReturnCache.putAsync(kk, vv, ttl, TimeUnit.MILLISECONDS); 75 | return Future.fromCompletionStage(completionStage, vertx.getOrCreateContext()).mapEmpty(); 76 | } 77 | 78 | @Override 79 | public Future putIfAbsent(K k, V v) { 80 | byte[] kk = DataConverter.toCachedObject(k); 81 | byte[] vv = DataConverter.toCachedObject(v); 82 | return Future.fromCompletionStage(cache.putIfAbsentAsync(kk, vv), vertx.getOrCreateContext()).map(DataConverter::fromCachedObject); 83 | } 84 | 85 | @Override 86 | public Future putIfAbsent(K k, V v, long ttl) { 87 | byte[] kk = DataConverter.toCachedObject(k); 88 | byte[] vv = DataConverter.toCachedObject(v); 89 | CompletableFuture completionStage = cache.putIfAbsentAsync(kk, vv, ttl, TimeUnit.MILLISECONDS); 90 | return Future.fromCompletionStage(completionStage, vertx.getOrCreateContext()).map(DataConverter::fromCachedObject); 91 | } 92 | 93 | @Override 94 | public Future remove(K k) { 95 | byte[] kk = DataConverter.toCachedObject(k); 96 | return Future.fromCompletionStage(cache.removeAsync(kk), vertx.getOrCreateContext()).map(DataConverter::fromCachedObject); 97 | } 98 | 99 | @Override 100 | public Future removeIfPresent(K k, V v) { 101 | byte[] kk = DataConverter.toCachedObject(k); 102 | byte[] vv = DataConverter.toCachedObject(v); 103 | return Future.fromCompletionStage(cache.removeAsync(kk, vv), vertx.getOrCreateContext()); 104 | } 105 | 106 | @Override 107 | public Future replace(K k, V v) { 108 | byte[] kk = DataConverter.toCachedObject(k); 109 | byte[] vv = DataConverter.toCachedObject(v); 110 | return Future.fromCompletionStage(cache.replaceAsync(kk, vv), vertx.getOrCreateContext()).map(DataConverter::fromCachedObject); 111 | } 112 | 113 | @Override 114 | public Future replace(K k, V v, long ttl) { 115 | byte[] kk = DataConverter.toCachedObject(k); 116 | byte[] vv = DataConverter.toCachedObject(v); 117 | return Future.fromCompletionStage(cache.replaceAsync(kk, vv, ttl, TimeUnit.MILLISECONDS), vertx.getOrCreateContext()).map(DataConverter::fromCachedObject); 118 | } 119 | 120 | @Override 121 | public Future replaceIfPresent(K k, V oldValue, V newValue) { 122 | byte[] kk = DataConverter.toCachedObject(k); 123 | byte[] oo = DataConverter.toCachedObject(oldValue); 124 | byte[] nn = DataConverter.toCachedObject(newValue); 125 | return Future.fromCompletionStage(cache.replaceAsync(kk, oo, nn), vertx.getOrCreateContext()); 126 | } 127 | 128 | @Override 129 | public Future replaceIfPresent(K k, V oldValue, V newValue, long ttl) { 130 | byte[] kk = DataConverter.toCachedObject(k); 131 | byte[] oo = DataConverter.toCachedObject(oldValue); 132 | byte[] nn = DataConverter.toCachedObject(newValue); 133 | return Future.fromCompletionStage(cache.replaceAsync(kk, oo, nn, ttl, TimeUnit.MILLISECONDS), vertx.getOrCreateContext()); 134 | } 135 | 136 | @Override 137 | public Future clear() { 138 | return Future.fromCompletionStage(cache.clearAsync(), vertx.getOrCreateContext()); 139 | } 140 | 141 | @Override 142 | public Future size() { 143 | return vertx.executeBlocking(cache::size, false); 144 | } 145 | 146 | @Override 147 | public Future> keys() { 148 | return vertx.executeBlocking(() -> { 149 | Set cacheKeys = cache.keySet().stream().collect(CacheCollectors.serializableCollector(Collectors::toSet)); 150 | return cacheKeys.stream().map(DataConverter::fromCachedObject).collect(toSet()); 151 | }, false); 152 | } 153 | 154 | @Override 155 | public Future> values() { 156 | return vertx.executeBlocking(() -> { 157 | List cacheValues = cache.values().stream().collect(CacheCollectors.serializableCollector(Collectors::toList)); 158 | return cacheValues.stream().map(DataConverter::fromCachedObject).collect(toList()); 159 | }, false); 160 | } 161 | 162 | @Override 163 | public Future> entries() { 164 | return vertx.executeBlocking(() -> { 165 | Map cacheEntries = cache.entrySet().stream() 166 | .collect(CacheCollectors.serializableCollector(() -> toMap(Entry::getKey, Entry::getValue))); 167 | Map result = new HashMap<>(); 168 | for (Entry entry : cacheEntries.entrySet()) { 169 | K k = DataConverter.fromCachedObject(entry.getKey()); 170 | V v = DataConverter.fromCachedObject(entry.getValue()); 171 | result.put(k, v); 172 | } 173 | return result; 174 | }, false); 175 | } 176 | 177 | @Override 178 | public ReadStream keyStream() { 179 | return new CloseableIteratorCollectionStream<>(vertx.getOrCreateContext(), cache::keySet, DataConverter::fromCachedObject); 180 | } 181 | 182 | @Override 183 | public ReadStream valueStream() { 184 | return new CloseableIteratorCollectionStream<>(vertx.getOrCreateContext(), cache::values, DataConverter::fromCachedObject); 185 | } 186 | 187 | @Override 188 | public ReadStream> entryStream() { 189 | return new CloseableIteratorCollectionStream<>(vertx.getOrCreateContext(), cache::entrySet, cacheEntry -> { 190 | K key = DataConverter.fromCachedObject(cacheEntry.getKey()); 191 | V value = DataConverter.fromCachedObject(cacheEntry.getValue()); 192 | return new SimpleImmutableEntry<>(key, value); 193 | }); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/InfinispanCounter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Future; 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.core.shareddata.Counter; 24 | import org.infinispan.counter.api.SyncStrongCounter; 25 | 26 | import java.util.Objects; 27 | 28 | /** 29 | * @author Thomas Segismont 30 | */ 31 | public class InfinispanCounter implements Counter { 32 | 33 | private final Vertx vertx; 34 | private final SyncStrongCounter strongCounter; 35 | 36 | public InfinispanCounter(Vertx vertx, SyncStrongCounter strongCounter) { 37 | this.vertx = vertx; 38 | this.strongCounter = strongCounter; 39 | } 40 | 41 | @Override 42 | public Future get() { 43 | return vertx.executeBlocking(strongCounter::getValue, false); 44 | } 45 | 46 | @Override 47 | public Future incrementAndGet() { 48 | return addAndGet(1L); 49 | } 50 | 51 | @Override 52 | public Future getAndIncrement() { 53 | return getAndAdd(1L); 54 | } 55 | 56 | @Override 57 | public Future decrementAndGet() { 58 | return addAndGet(-1L); 59 | } 60 | 61 | @Override 62 | public Future addAndGet(long value) { 63 | return vertx.executeBlocking(() -> strongCounter.addAndGet(value), false); 64 | } 65 | 66 | @Override 67 | public Future getAndAdd(long value) { 68 | return vertx.executeBlocking(() -> strongCounter.addAndGet(value) - value, false); 69 | } 70 | 71 | @Override 72 | public Future compareAndSet(long expected, long value) { 73 | return vertx.executeBlocking(() -> strongCounter.compareAndSet(expected, value), false); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/InfinispanLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.shareddata.Lock; 20 | import org.infinispan.lock.api.ClusteredLock; 21 | 22 | public class InfinispanLock implements Lock { 23 | 24 | private final ClusteredLock lock; 25 | 26 | public InfinispanLock(ClusteredLock lock) { 27 | this.lock = lock; 28 | } 29 | 30 | @Override 31 | public void release() { 32 | lock.unlock(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/SubsCacheHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.internal.VertxInternal; 20 | import io.vertx.core.internal.logging.Logger; 21 | import io.vertx.core.internal.logging.LoggerFactory; 22 | import io.vertx.core.spi.cluster.RegistrationInfo; 23 | import io.vertx.core.spi.cluster.RegistrationListener; 24 | import io.vertx.core.spi.cluster.RegistrationUpdateEvent; 25 | import org.infinispan.manager.DefaultCacheManager; 26 | import org.infinispan.metadata.Metadata; 27 | import org.infinispan.multimap.api.embedded.EmbeddedMultimapCacheManagerFactory; 28 | import org.infinispan.multimap.api.embedded.MultimapCacheManager; 29 | import org.infinispan.multimap.impl.Bucket; 30 | import org.infinispan.multimap.impl.EmbeddedMultimapCache; 31 | import org.infinispan.notifications.Listener; 32 | import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; 33 | import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified; 34 | import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; 35 | import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent; 36 | import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent; 37 | import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent; 38 | import org.infinispan.notifications.cachelistener.filter.CacheEventConverter; 39 | import org.infinispan.notifications.cachelistener.filter.CacheEventFilter; 40 | import org.infinispan.notifications.cachelistener.filter.EventType; 41 | import org.infinispan.util.function.SerializablePredicate; 42 | 43 | import java.lang.annotation.Annotation; 44 | import java.util.*; 45 | import java.util.concurrent.CompletableFuture; 46 | import java.util.concurrent.ConcurrentHashMap; 47 | import java.util.concurrent.ConcurrentMap; 48 | import java.util.stream.Stream; 49 | 50 | import static java.util.stream.Collectors.toSet; 51 | import static org.infinispan.notifications.Listener.Observation.POST; 52 | 53 | /** 54 | * @author Thomas Segismont 55 | */ 56 | public class SubsCacheHelper { 57 | 58 | private static final Logger log = LoggerFactory.getLogger(SubsCacheHelper.class); 59 | 60 | private final VertxInternal vertx; 61 | private final Throttling throttling; 62 | private final EmbeddedMultimapCache subsCache; 63 | private final RegistrationListener nodeSelector; 64 | private final EntryListener entryListener; 65 | 66 | private final ConcurrentMap> localSubs = new ConcurrentHashMap<>(); 67 | 68 | public SubsCacheHelper(VertxInternal vertx, DefaultCacheManager cacheManager, RegistrationListener nodeSelector) { 69 | this.vertx = vertx; 70 | throttling = new Throttling(vertx, this::getAndUpdate); 71 | @SuppressWarnings("unchecked") 72 | MultimapCacheManager multimapCacheManager = EmbeddedMultimapCacheManagerFactory.from(cacheManager); 73 | subsCache = (EmbeddedMultimapCache) multimapCacheManager.get("__vertx.subs"); 74 | this.nodeSelector = nodeSelector; 75 | entryListener = new EntryListener(); 76 | Set> filterAnnotations = Stream.>builder() 77 | .add(CacheEntryCreated.class) 78 | .add(CacheEntryModified.class) 79 | .add(CacheEntryRemoved.class) 80 | .build() 81 | .collect(toSet()); 82 | subsCache.getCache() 83 | .addFilteredListener(entryListener, new EventFilter(), new EventConverter(), filterAnnotations); 84 | } 85 | 86 | public CompletableFuture> get(String address) { 87 | return subsCache.get(address) 88 | .thenApply(remote -> { 89 | List list; 90 | int size; 91 | size = remote.size(); 92 | Set local = localSubs.get(address); 93 | if (local != null) { 94 | synchronized (local) { 95 | size += local.size(); 96 | if (size == 0) { 97 | return Collections.emptyList(); 98 | } 99 | list = new ArrayList<>(size); 100 | list.addAll(local); 101 | } 102 | } else if (size == 0) { 103 | return Collections.emptyList(); 104 | } else { 105 | list = new ArrayList<>(size); 106 | } 107 | for (byte[] value : remote) { 108 | list.add(DataConverter.fromCachedObject(value)); 109 | } 110 | return list; 111 | }); 112 | } 113 | 114 | public CompletableFuture put(String address, RegistrationInfo registrationInfo) { 115 | if (registrationInfo.localOnly()) { 116 | localSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr)); 117 | vertx.getOrCreateContext().runOnContext(v -> fireRegistrationUpdateEvent(address)); 118 | return CompletableFuture.completedFuture(null); 119 | } else { 120 | return subsCache.put(address, DataConverter.toCachedObject(registrationInfo)); 121 | } 122 | } 123 | 124 | private Set addToSet(RegistrationInfo registrationInfo, Set curr) { 125 | Set res = curr != null ? curr : Collections.synchronizedSet(new LinkedHashSet<>()); 126 | res.add(registrationInfo); 127 | return res; 128 | } 129 | 130 | public CompletableFuture remove(String address, RegistrationInfo registrationInfo) { 131 | if (registrationInfo.localOnly()) { 132 | localSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr)); 133 | vertx.getOrCreateContext().runOnContext(v -> fireRegistrationUpdateEvent(address)); 134 | return CompletableFuture.completedFuture(null); 135 | } else { 136 | return subsCache.remove(address, DataConverter.toCachedObject(registrationInfo)).thenApply(v -> null); 137 | } 138 | } 139 | 140 | private Set removeFromSet(RegistrationInfo registrationInfo, Set curr) { 141 | curr.remove(registrationInfo); 142 | return curr.isEmpty() ? null : curr; 143 | } 144 | 145 | public void removeAllForNode(String nodeId) { 146 | subsCache.remove((SerializablePredicate) value -> nodeId.equals(DataConverter.fromCachedObject(value).nodeId())); 147 | } 148 | 149 | public void close() { 150 | subsCache.getCache().removeListener(entryListener); 151 | } 152 | 153 | private void fireRegistrationUpdateEvent(String address) { 154 | throttling.onEvent(address); 155 | } 156 | 157 | private CompletableFuture getAndUpdate(String address) { 158 | if (nodeSelector.wantsUpdatesFor(address)) { 159 | return get(address).whenComplete((registrationInfos, throwable) -> { 160 | if (throwable == null) { 161 | nodeSelector.registrationsUpdated(new RegistrationUpdateEvent(address, registrationInfos)); 162 | } else { 163 | log.trace("A failure occurred while retrieving the updated registrations", throwable); 164 | nodeSelector.registrationsUpdated(new RegistrationUpdateEvent(address, Collections.emptyList())); 165 | } 166 | }); 167 | } 168 | return CompletableFuture.completedFuture(null); 169 | } 170 | 171 | @Listener(clustered = true, observation = POST, sync = false) 172 | private class EntryListener { 173 | 174 | public EntryListener() { 175 | } 176 | 177 | @CacheEntryCreated 178 | public void entryCreated(CacheEntryCreatedEvent event) { 179 | fireRegistrationUpdateEvent(event.getKey()); 180 | } 181 | 182 | @CacheEntryModified 183 | public void entryModified(CacheEntryModifiedEvent event) { 184 | fireRegistrationUpdateEvent(event.getKey()); 185 | } 186 | 187 | @CacheEntryRemoved 188 | public void entryRemoved(CacheEntryRemovedEvent event) { 189 | fireRegistrationUpdateEvent(event.getKey()); 190 | } 191 | } 192 | 193 | private static class EventFilter implements CacheEventFilter> { 194 | 195 | public EventFilter() { 196 | } 197 | 198 | @Override 199 | public boolean accept(String key, Bucket oldValue, Metadata oldMetadata, Bucket newValue, Metadata newMetadata, EventType eventType) { 200 | return true; 201 | } 202 | } 203 | 204 | private static class EventConverter implements CacheEventConverter, Void> { 205 | 206 | @Override 207 | public Void convert(String key, Bucket oldValue, Metadata oldMetadata, Bucket newValue, Metadata newMetadata, EventType eventType) { 208 | return null; 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/SubsOpSerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.Completable; 20 | import io.vertx.core.Promise; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.internal.ContextInternal; 23 | import io.vertx.core.spi.cluster.RegistrationInfo; 24 | 25 | import java.util.LinkedList; 26 | import java.util.Queue; 27 | import java.util.concurrent.CompletableFuture; 28 | import java.util.concurrent.ConcurrentMap; 29 | import java.util.concurrent.Executor; 30 | import java.util.function.BiConsumer; 31 | import java.util.function.BiFunction; 32 | 33 | /** 34 | * @author Thomas Segismont 35 | */ 36 | public class SubsOpSerializer implements BiConsumer { 37 | 38 | private final ContextInternal context; 39 | private final Executor executor; 40 | private final Queue tasks; 41 | 42 | private SubsOpSerializer(ContextInternal context) { 43 | this.context = context; 44 | this.executor = task -> context.runOnContext(v -> task.run()); 45 | tasks = new LinkedList<>(); 46 | } 47 | 48 | public static SubsOpSerializer get(ContextInternal context) { 49 | ConcurrentMap contextData = context.contextData(); 50 | SubsOpSerializer instance = (SubsOpSerializer) contextData.get(SubsOpSerializer.class); 51 | if (instance == null) { 52 | SubsOpSerializer candidate = new SubsOpSerializer(context); 53 | SubsOpSerializer previous = (SubsOpSerializer) contextData.putIfAbsent(SubsOpSerializer.class, candidate); 54 | instance = previous == null ? candidate : previous; 55 | } 56 | return instance; 57 | } 58 | 59 | public void execute(BiFunction> op, String address, RegistrationInfo registrationInfo, Completable promise) { 60 | if (Vertx.currentContext() != context) { 61 | context.runOnContext(v -> execute(op, address, registrationInfo, promise)); 62 | return; 63 | } 64 | tasks.add(new Task(op, address, registrationInfo, promise)); 65 | if (tasks.size() == 1) { 66 | processTask(tasks.peek()); 67 | } 68 | } 69 | 70 | private void processTask(Task task) { 71 | if (Vertx.currentContext() != context) { 72 | throw new IllegalStateException(); 73 | } 74 | CompletableFuture future = task.op.apply(task.address, task.registrationInfo); 75 | future.whenCompleteAsync(this, executor); 76 | } 77 | 78 | @Override 79 | public void accept(Object o, Throwable throwable) { 80 | if (Vertx.currentContext() != context) { 81 | throw new IllegalStateException(); 82 | } 83 | Task task = tasks.remove(); 84 | if (throwable == null) { 85 | task.promise.succeed(); 86 | } else { 87 | task.promise.fail(throwable); 88 | } 89 | Task next = tasks.peek(); 90 | if (next != null) { 91 | processTask(next); 92 | } 93 | } 94 | 95 | private static class Task { 96 | final BiFunction> op; 97 | final String address; 98 | final RegistrationInfo registrationInfo; 99 | final Completable promise; 100 | 101 | Task(BiFunction> op, String address, RegistrationInfo registrationInfo, Completable promise) { 102 | this.op = op; 103 | this.address = address; 104 | this.registrationInfo = registrationInfo; 105 | this.promise = promise; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/impl/Throttling.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.internal.VertxInternal; 20 | 21 | import java.util.concurrent.CompletableFuture; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | import java.util.concurrent.ConcurrentMap; 24 | import java.util.function.Function; 25 | 26 | public class Throttling { 27 | 28 | // @formatter:off 29 | private enum State { 30 | NEW { 31 | State pending() { return PENDING; } 32 | State start() { return RUNNING; } 33 | State done() { throw new IllegalStateException(); } 34 | State next() { throw new IllegalStateException(); } 35 | }, 36 | PENDING { 37 | State pending() { return this; } 38 | State start() { return RUNNING; } 39 | State done() { throw new IllegalStateException(); } 40 | State next() { throw new IllegalStateException(); } 41 | }, 42 | RUNNING { 43 | State pending() { return RUNNING_PENDING; } 44 | State start() { throw new IllegalStateException(); } 45 | State done() { return FINISHED; } 46 | State next() { throw new IllegalStateException(); } 47 | }, 48 | RUNNING_PENDING { 49 | State pending() { return this; } 50 | State start() { throw new IllegalStateException(); } 51 | State done() { return FINISHED_PENDING; } 52 | State next() { throw new IllegalStateException(); } 53 | }, 54 | FINISHED { 55 | State pending() { return FINISHED_PENDING; } 56 | State start() { throw new IllegalStateException(); } 57 | State done() { throw new IllegalStateException(); } 58 | State next() { return null; } 59 | }, 60 | FINISHED_PENDING { 61 | State pending() { return this; } 62 | State start() { throw new IllegalStateException(); } 63 | State done() { throw new IllegalStateException(); } 64 | State next() { return NEW; } 65 | }; 66 | 67 | abstract State pending(); 68 | abstract State start(); 69 | abstract State done(); 70 | abstract State next(); 71 | } 72 | // @formatter:on 73 | 74 | private final VertxInternal vertx; 75 | private final Function> action; 76 | private final ConcurrentMap map; 77 | 78 | public Throttling(VertxInternal vertx, Function> action) { 79 | this.vertx = vertx; 80 | this.action = action; 81 | map = new ConcurrentHashMap<>(); 82 | } 83 | 84 | public void onEvent(String address) { 85 | State curr = map.compute(address, (s, state) -> state == null ? State.NEW : state.pending()); 86 | if (curr == State.NEW) { 87 | run(address); 88 | } 89 | } 90 | 91 | private void run(String address) { 92 | map.computeIfPresent(address, (s, state) -> state.start()); 93 | action.apply(address).whenComplete((v, throwable) -> { 94 | map.computeIfPresent(address, (s, state) -> state.done()); 95 | vertx.setTimer(20, l -> { 96 | checkState(address); 97 | }); 98 | }); 99 | } 100 | 101 | private void checkState(String address) { 102 | State curr = map.computeIfPresent(address, (s, state) -> state.next()); 103 | if (curr == State.NEW) { 104 | run(address); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/java/io/vertx/ext/cluster/infinispan/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | @ModuleGen(name = "vertx-infinispan", groupPackage = "io.vertx") 18 | package io.vertx.ext.cluster.infinispan; 19 | 20 | import io.vertx.codegen.annotations.ModuleGen; 21 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat, Inc. 3 | # 4 | # Red Hat licenses this file to you under the Apache License, version 2.0 5 | # (the "License"); you may not use this file except in compliance with the 6 | # License. You may obtain a copy of the License at: 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | # 16 | 17 | io.vertx.ext.cluster.infinispan.InfinispanClusterManager 18 | -------------------------------------------------------------------------------- /vertx-infinispan/src/main/resources/default-infinispan.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/Lifecycle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.internal.VertxInternal; 21 | import io.vertx.core.internal.logging.Logger; 22 | import io.vertx.core.internal.logging.LoggerFactory; 23 | import io.vertx.core.spi.cluster.ClusterManager; 24 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 25 | import io.vertx.tests.eventbus.WrappedClusterManager; 26 | import org.infinispan.health.Health; 27 | import org.infinispan.health.HealthStatus; 28 | import org.infinispan.lifecycle.ComponentStatus; 29 | import org.infinispan.manager.EmbeddedCacheManager; 30 | 31 | import java.util.List; 32 | import java.util.concurrent.CountDownLatch; 33 | import java.util.concurrent.TimeUnit; 34 | 35 | import static java.util.concurrent.TimeUnit.*; 36 | import static org.infinispan.lifecycle.ComponentStatus.STOPPING; 37 | 38 | /** 39 | * @author Thomas Segismont 40 | */ 41 | public class Lifecycle { 42 | 43 | private static final Logger log = LoggerFactory.getLogger(Lifecycle.class); 44 | 45 | public static void close(List clustered) throws Exception { 46 | for (Vertx vertx : clustered) { 47 | VertxInternal vertxInternal = (VertxInternal) vertx; 48 | 49 | InfinispanClusterManager clusterManager = getInfinispanClusterManager(vertxInternal.clusterManager()); 50 | 51 | ComponentStatus status = null; 52 | if (clusterManager != null) { 53 | EmbeddedCacheManager cacheManager = (EmbeddedCacheManager) clusterManager.getCacheContainer(); 54 | status = cacheManager.getStatus(); 55 | 56 | Health health = cacheManager.getHealth(); 57 | 58 | SECONDS.sleep(2); // Make sure rebalancing has been triggered 59 | 60 | long start = System.currentTimeMillis(); 61 | try { 62 | while (health.getClusterHealth().getHealthStatus() != HealthStatus.HEALTHY 63 | && System.currentTimeMillis() - start < MILLISECONDS.convert(2, MINUTES)) { 64 | MILLISECONDS.sleep(100); 65 | } 66 | } catch (Exception ignore) { 67 | } 68 | } 69 | 70 | if (status == null || status.compareTo(STOPPING) >= 0) { 71 | vertxInternal.close(); 72 | } else { 73 | CountDownLatch latch = new CountDownLatch(1); 74 | vertxInternal.close().onComplete(ar -> { 75 | if (ar.failed()) { 76 | log.error("Failed to shutdown vert.x", ar.cause()); 77 | } 78 | latch.countDown(); 79 | }); 80 | latch.await(2, TimeUnit.MINUTES); 81 | } 82 | } 83 | } 84 | 85 | private static InfinispanClusterManager getInfinispanClusterManager(ClusterManager cm) { 86 | if (cm == null) { 87 | return null; 88 | } 89 | if (cm instanceof WrappedClusterManager) { 90 | return getInfinispanClusterManager(((WrappedClusterManager) cm).getDelegate()); 91 | } 92 | if (cm instanceof InfinispanClusterManager) { 93 | return (InfinispanClusterManager) cm; 94 | } 95 | throw new ClassCastException("Unexpected cluster manager implementation: " + cm.getClass()); 96 | } 97 | 98 | private Lifecycle() { 99 | // Utility 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/LoggingTestWatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx; 18 | 19 | import org.junit.rules.TestWatcher; 20 | import org.junit.runner.Description; 21 | 22 | /** 23 | * @author Thomas Segismont 24 | */ 25 | public class LoggingTestWatcher extends TestWatcher { 26 | 27 | @Override 28 | protected void starting(Description description) { 29 | System.out.printf("Running %s#%s %s", description.getClassName(), description.getMethodName(), System.getProperty("line.separator")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/InfinispanComplexHATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.core.spi.cluster.ClusterManager; 21 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 22 | import org.junit.Rule; 23 | import org.junit.rules.TemporaryFolder; 24 | 25 | import java.util.List; 26 | import java.util.concurrent.CountDownLatch; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | /** 30 | * @author Thomas Segismont 31 | */ 32 | public class InfinispanComplexHATest extends io.vertx.tests.ha.ComplexHATest { 33 | 34 | @Rule 35 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 36 | 37 | @Override 38 | public void setUp() throws Exception { 39 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 40 | super.setUp(); 41 | } 42 | 43 | @Override 44 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 45 | Future ret = super.clusteredVertx(options, clusterManager); 46 | try { 47 | ret.await(2, TimeUnit.MINUTES); 48 | } catch (Exception e) { 49 | fail(e.getMessage()); 50 | } 51 | return ret; 52 | } 53 | 54 | @Override 55 | protected ClusterManager getClusterManager() { 56 | return new InfinispanClusterManager(); 57 | } 58 | 59 | @Override 60 | protected void close(List clustered) throws Exception { 61 | Lifecycle.close(clustered); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/InfinispanHATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.spi.cluster.ClusterManager; 22 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 23 | import org.junit.Rule; 24 | import org.junit.rules.TemporaryFolder; 25 | 26 | import java.util.List; 27 | import java.util.concurrent.CountDownLatch; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * @author Thomas Segismont 32 | */ 33 | public class InfinispanHATest extends io.vertx.tests.ha.HATest { 34 | 35 | @Rule 36 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 37 | 38 | @Rule 39 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 40 | 41 | @Override 42 | public void setUp() throws Exception { 43 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 44 | super.setUp(); 45 | } 46 | 47 | @Override 48 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 49 | Future ret = super.clusteredVertx(options, clusterManager); 50 | try { 51 | ret.await(2, TimeUnit.MINUTES); 52 | } catch (Exception e) { 53 | fail(e.getMessage()); 54 | } 55 | return ret; 56 | } 57 | 58 | @Override 59 | protected ClusterManager getClusterManager() { 60 | return new InfinispanClusterManager(); 61 | } 62 | 63 | @Override 64 | protected void close(List clustered) throws Exception { 65 | Lifecycle.close(clustered); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/eventbus/InfinispanClusteredEventBusTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.*; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 24 | import org.junit.Rule; 25 | import org.junit.rules.TemporaryFolder; 26 | 27 | import java.util.List; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * @author Thomas Segismont 33 | */ 34 | public class InfinispanClusteredEventBusTest extends io.vertx.tests.eventbus.ClusteredEventBusTest { 35 | 36 | @Rule 37 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 38 | 39 | @Rule 40 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 41 | 42 | @Override 43 | public void setUp() throws Exception { 44 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 45 | super.setUp(); 46 | } 47 | 48 | @Override 49 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 50 | Future ret = super.clusteredVertx(options, clusterManager); 51 | try { 52 | ret.await(2, TimeUnit.MINUTES); 53 | } catch (Exception e) { 54 | fail(e.getMessage()); 55 | } 56 | return ret; 57 | } 58 | 59 | @Override 60 | protected ClusterManager getClusterManager() { 61 | return new InfinispanClusterManager(); 62 | } 63 | 64 | @Override 65 | protected void close(List clustered) throws Exception { 66 | Lifecycle.close(clustered); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/eventbus/InfinispanFaultToleranceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.core.*; 20 | import io.vertx.core.spi.cluster.ClusterManager; 21 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 22 | import org.junit.Rule; 23 | import org.junit.rules.TemporaryFolder; 24 | 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.concurrent.CountDownLatch; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * @author Thomas Segismont 32 | */ 33 | public class InfinispanFaultToleranceTest extends io.vertx.tests.eventbus.FaultToleranceTest { 34 | 35 | @Rule 36 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 37 | 38 | @Override 39 | public void setUp() throws Exception { 40 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 41 | super.setUp(); 42 | } 43 | 44 | @Override 45 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 46 | Future ret = super.clusteredVertx(options, clusterManager); 47 | try { 48 | ret.await(2, TimeUnit.MINUTES); 49 | } catch (Exception e) { 50 | fail(e.getMessage()); 51 | } 52 | return ret; 53 | } 54 | 55 | @Override 56 | protected ClusterManager getClusterManager() { 57 | return new InfinispanClusterManager(); 58 | } 59 | 60 | @Override 61 | protected List getExternalNodeSystemProperties() { 62 | return Arrays.asList( 63 | "-Djava.net.preferIPv4Stack=true", 64 | "-Djgroups.join_timeout=1000", 65 | "-Dvertx.infinispan.config=infinispan.xml", 66 | "-Dvertx.jgroups.config=jgroups.xml", 67 | "-Djgroups.file.location=" + System.getProperty("jgroups.file.location"), 68 | "-Dvertx.logger-delegate-factory-class-name=io.vertx.core.logging.SLF4JLogDelegateFactory" 69 | ); 70 | } 71 | 72 | @Override 73 | protected void afterNodesKilled() throws Exception { 74 | super.afterNodesKilled(); 75 | // Additionnal wait to make sure all nodes noticed the shutdowns 76 | Thread.sleep(30_000); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/eventbus/InfinispanNodeInfoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.*; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 24 | import org.junit.Rule; 25 | import org.junit.rules.TemporaryFolder; 26 | 27 | import java.util.List; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * @author Thomas Segismont 33 | */ 34 | public class InfinispanNodeInfoTest extends io.vertx.tests.eventbus.NodeInfoTest { 35 | 36 | @Rule 37 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 38 | 39 | @Rule 40 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 41 | 42 | @Override 43 | public void setUp() throws Exception { 44 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 45 | super.setUp(); 46 | } 47 | 48 | @Override 49 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 50 | Future ret = super.clusteredVertx(options, clusterManager); 51 | try { 52 | ret.await(2, TimeUnit.MINUTES); 53 | } catch (Exception e) { 54 | fail(e.getMessage()); 55 | } 56 | return ret; 57 | } 58 | 59 | @Override 60 | protected ClusterManager getClusterManager() { 61 | return new InfinispanClusterManager(); 62 | } 63 | 64 | @Override 65 | protected void close(List clustered) throws Exception { 66 | Lifecycle.close(clustered); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/servicediscovery/impl/InfinispanDiscoveryImplClusteredTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.servicediscovery.impl; 18 | 19 | import io.vertx.LoggingTestWatcher; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.VertxOptions; 22 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 23 | import io.vertx.servicediscovery.ServiceDiscoveryOptions; 24 | import io.vertx.servicediscovery.impl.DiscoveryImpl; 25 | import io.vertx.servicediscovery.impl.DiscoveryImplTestBase; 26 | import org.junit.Before; 27 | import org.junit.Rule; 28 | import org.junit.rules.TemporaryFolder; 29 | 30 | import static com.jayway.awaitility.Awaitility.await; 31 | 32 | /** 33 | * @author Thomas Segismont 34 | */ 35 | public class InfinispanDiscoveryImplClusteredTest extends DiscoveryImplTestBase { 36 | 37 | @Rule 38 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 39 | 40 | @Rule 41 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 42 | 43 | @Before 44 | public void setUp() throws Exception { 45 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 46 | 47 | Vertx.builder().withClusterManager(new InfinispanClusterManager()).buildClustered().onComplete(ar -> { 48 | vertx = ar.result(); 49 | }); 50 | await().until(() -> vertx != null); 51 | discovery = new DiscoveryImpl(vertx, new ServiceDiscoveryOptions()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/shareddata/InfinispanClusteredAsyncMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.*; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.json.JsonObject; 24 | import io.vertx.core.spi.cluster.ClusterManager; 25 | import io.vertx.core.streams.ReadStream; 26 | import io.vertx.ext.cluster.infinispan.InfinispanAsyncMap; 27 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 28 | import org.junit.Rule; 29 | import org.junit.Test; 30 | import org.junit.rules.TemporaryFolder; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.concurrent.CountDownLatch; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.concurrent.atomic.AtomicInteger; 38 | import java.util.function.BiConsumer; 39 | import java.util.function.Function; 40 | 41 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 42 | 43 | /** 44 | * @author Thomas Segismont 45 | */ 46 | public class InfinispanClusteredAsyncMapTest extends io.vertx.tests.shareddata.ClusteredAsyncMapTest { 47 | 48 | @Rule 49 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 50 | 51 | @Rule 52 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 53 | 54 | @Override 55 | public void setUp() throws Exception { 56 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 57 | super.setUp(); 58 | } 59 | 60 | @Override 61 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 62 | Future ret = super.clusteredVertx(options, clusterManager); 63 | try { 64 | ret.await(2, TimeUnit.MINUTES); 65 | } catch (Exception e) { 66 | fail(e.getMessage()); 67 | } 68 | return ret; 69 | } 70 | 71 | @Override 72 | protected ClusterManager getClusterManager() { 73 | return new InfinispanClusterManager(); 74 | } 75 | 76 | @Test 77 | public void testKeyStream() { 78 | testReadStream(InfinispanAsyncMap::keyStream, (map, keys) -> { 79 | assertEquals(map.size(), keys.size()); 80 | assertTrue(keys.containsAll(map.keySet())); 81 | }); 82 | } 83 | 84 | @Test 85 | public void testValueStream() { 86 | testReadStream(InfinispanAsyncMap::valueStream, (map, values) -> { 87 | assertEquals(map.size(), values.size()); 88 | assertTrue(values.containsAll(map.values())); 89 | assertTrue(map.values().containsAll(values)); 90 | }); 91 | } 92 | 93 | @Test 94 | public void testEntryStream() { 95 | testReadStream(InfinispanAsyncMap::entryStream, (map, entries) -> { 96 | assertEquals(map.size(), entries.size()); 97 | assertTrue(entries.containsAll(map.entrySet())); 98 | }); 99 | } 100 | 101 | private void testReadStream( 102 | Function, ReadStream> streamFactory, 103 | BiConsumer, List> assertions 104 | ) { 105 | Map map = genJsonToBuffer(100); 106 | loadData(map, (vertx, asyncMap) -> { 107 | List items = new ArrayList<>(); 108 | ReadStream stream = streamFactory.apply(InfinispanAsyncMap.unwrap(asyncMap)); 109 | AtomicInteger idx = new AtomicInteger(); 110 | long pause = 500; 111 | long start = System.nanoTime(); 112 | stream.endHandler(end -> { 113 | assertions.accept(map, items); 114 | long duration = NANOSECONDS.toMillis(System.nanoTime() - start); 115 | assertTrue(duration >= 3 * pause); 116 | testComplete(); 117 | }).exceptionHandler(t -> { 118 | fail(t); 119 | }).handler(item -> { 120 | items.add(item); 121 | int j = idx.getAndIncrement(); 122 | if (j == 3 || j == 16 || j == 38) { 123 | stream.pause(); 124 | int emitted = items.size(); 125 | vertx.setTimer(pause, tid -> { 126 | assertTrue("Items emitted during pause", emitted == items.size()); 127 | stream.resume(); 128 | }); 129 | } 130 | }); 131 | }); 132 | await(); 133 | } 134 | 135 | @Test 136 | public void testClosedKeyStream() { 137 | Map map = genJsonToBuffer(100); 138 | loadData(map, (vertx, asyncMap) -> { 139 | List keys = new ArrayList<>(); 140 | ReadStream stream = InfinispanAsyncMap.unwrap(asyncMap).keyStream(); 141 | stream.exceptionHandler(t -> { 142 | fail(t); 143 | }).handler(jsonObject -> { 144 | keys.add(jsonObject); 145 | if (jsonObject.getInteger("key") == 38) { 146 | stream.handler(null); 147 | int emitted = keys.size(); 148 | vertx.setTimer(500, tid -> { 149 | assertTrue("Items emitted after close", emitted == keys.size()); 150 | testComplete(); 151 | }); 152 | } 153 | }); 154 | }); 155 | await(); 156 | } 157 | 158 | @Override 159 | protected void close(List clustered) throws Exception { 160 | Lifecycle.close(clustered); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/shareddata/InfinispanClusteredAsynchronousLockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.Future; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.core.VertxOptions; 24 | import io.vertx.core.spi.cluster.ClusterManager; 25 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 26 | import org.junit.Rule; 27 | import org.junit.Test; 28 | import org.junit.rules.TemporaryFolder; 29 | 30 | import java.util.List; 31 | import java.util.concurrent.TimeUnit; 32 | 33 | /** 34 | * @author Thomas Segismont 35 | */ 36 | public class InfinispanClusteredAsynchronousLockTest extends io.vertx.tests.shareddata.ClusteredAsynchronousLockTest { 37 | 38 | @Rule 39 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 40 | 41 | @Rule 42 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 43 | 44 | @Override 45 | public void setUp() throws Exception { 46 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 47 | super.setUp(); 48 | } 49 | 50 | @Override 51 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 52 | Future ret = super.clusteredVertx(options, clusterManager); 53 | try { 54 | ret.await(2, TimeUnit.MINUTES); 55 | } catch (Exception e) { 56 | fail(e.getMessage()); 57 | } 58 | return ret; 59 | } 60 | 61 | @Override 62 | protected ClusterManager getClusterManager() { 63 | return new InfinispanClusterManager(); 64 | } 65 | 66 | @Override 67 | @Test 68 | public void testLockReleasedForClosedNode() throws Exception { 69 | super.testLockReleasedForClosedNode(); 70 | } 71 | 72 | @Override 73 | @Test 74 | public void testLockReleasedForKilledNode() throws Exception { 75 | super.testLockReleasedForKilledNode(); 76 | } 77 | 78 | @Override 79 | protected long getLockTimeout() { 80 | return 30000; 81 | } 82 | 83 | @Override 84 | protected void close(List clustered) throws Exception { 85 | Lifecycle.close(clustered); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/core/shareddata/InfinispanClusteredSharedCounterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.*; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 24 | import org.junit.Rule; 25 | import org.junit.rules.TemporaryFolder; 26 | 27 | import java.util.List; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * @author Thomas Segismont 33 | */ 34 | public class InfinispanClusteredSharedCounterTest extends io.vertx.tests.shareddata.ClusteredSharedCounterTest { 35 | 36 | @Rule 37 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 38 | 39 | @Rule 40 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 41 | 42 | @Override 43 | public void setUp() throws Exception { 44 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 45 | super.setUp(); 46 | } 47 | 48 | @Override 49 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 50 | Future ret = super.clusteredVertx(options, clusterManager); 51 | try { 52 | ret.await(2, TimeUnit.MINUTES); 53 | } catch (Exception e) { 54 | fail(e.getMessage()); 55 | } 56 | return ret; 57 | } 58 | 59 | @Override 60 | protected ClusterManager getClusterManager() { 61 | return new InfinispanClusterManager(); 62 | } 63 | 64 | @Override 65 | protected void close(List clustered) throws Exception { 66 | Lifecycle.close(clustered); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/ext/cluster/infinispan/ClusterHealthCheckTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.core.*; 21 | import io.vertx.core.json.JsonObject; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.ext.healthchecks.Status; 24 | import io.vertx.test.core.VertxTestBase; 25 | import org.junit.Rule; 26 | import org.junit.Test; 27 | import org.junit.rules.TemporaryFolder; 28 | 29 | import java.util.List; 30 | import java.util.concurrent.CountDownLatch; 31 | import java.util.concurrent.TimeUnit; 32 | 33 | import static org.hamcrest.CoreMatchers.anyOf; 34 | import static org.hamcrest.CoreMatchers.is; 35 | import static org.infinispan.health.HealthStatus.HEALTHY; 36 | import static org.infinispan.health.HealthStatus.HEALTHY_REBALANCING; 37 | 38 | public class ClusterHealthCheckTest extends VertxTestBase { 39 | 40 | @Rule 41 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 42 | 43 | @Override 44 | public void setUp() throws Exception { 45 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 46 | super.setUp(); 47 | } 48 | 49 | @Override 50 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 51 | Future ret = super.clusteredVertx(options, clusterManager); 52 | try { 53 | ret.await(2, TimeUnit.MINUTES); 54 | } catch (Exception e) { 55 | fail(e.getMessage()); 56 | } 57 | return ret; 58 | } 59 | 60 | @Override 61 | protected ClusterManager getClusterManager() { 62 | return new InfinispanClusterManager(); 63 | } 64 | 65 | @Override 66 | protected void close(List clustered) throws Exception { 67 | Lifecycle.close(clustered); 68 | } 69 | 70 | @Test 71 | public void testDetailedHealthCheck() { 72 | startNodes(2); 73 | ClusterHealthCheck healthCheck = ClusterHealthCheck.createProcedure(vertices[1], true); 74 | vertices[0].sharedData().getAsyncMap("foo").onComplete(onSuccess(asyncMap -> { 75 | vertices[1].executeBlocking(() -> { 76 | Promise promise = Promise.promise(); 77 | healthCheck.handle(promise); 78 | return promise.future().toCompletionStage().toCompletableFuture().get(); 79 | }).onComplete(onSuccess(status -> { 80 | JsonObject json = new JsonObject(status.toJson().encode()); // test serialization+deserialization 81 | assertTrue(json.getBoolean("ok")); 82 | assertEquals(Integer.valueOf(2), json.getJsonObject("data").getJsonObject("clusterHealth").getInteger("numberOfNodes")); 83 | String fooCacheHealth = json.getJsonObject("data").getJsonArray("cacheHealth").stream() 84 | .map(JsonObject.class::cast) 85 | .filter(entry -> entry.getString("cacheName").equals("foo")) 86 | .findAny() 87 | .map(jsonObject -> jsonObject.getString("status")) 88 | .orElseThrow(() -> new AssertionError("foo cache is missing")); 89 | assertThat(fooCacheHealth, anyOf(is(HEALTHY.name()), is(HEALTHY_REBALANCING.name()))); 90 | testComplete(); 91 | })); 92 | })); 93 | await(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/ext/cluster/infinispan/impl/ThrottlingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.cluster.infinispan.impl; 18 | 19 | import io.vertx.core.internal.VertxInternal; 20 | import io.vertx.test.core.VertxTestBase; 21 | import org.junit.Test; 22 | 23 | import java.util.Collections; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | import java.util.concurrent.*; 27 | 28 | import static java.util.concurrent.TimeUnit.*; 29 | 30 | public class ThrottlingTest extends VertxTestBase { 31 | 32 | int threadCount = 4; 33 | ExecutorService executorService; 34 | 35 | @Override 36 | public void setUp() throws Exception { 37 | super.setUp(); 38 | executorService = Executors.newFixedThreadPool(threadCount); 39 | } 40 | 41 | @Test 42 | public void testInterval() throws Exception { 43 | int duration = 5; 44 | String[] addresses = {"foo", "bar", "baz", "qux"}; 45 | 46 | ConcurrentMap> events = new ConcurrentHashMap<>(addresses.length); 47 | Throttling throttling = new Throttling((VertxInternal) vertx, address -> { 48 | events.compute(address, (k, v) -> { 49 | if (v == null) { 50 | v = Collections.synchronizedList(new LinkedList<>()); 51 | } 52 | v.add(System.nanoTime()); 53 | return v; 54 | }); 55 | CompletableFuture future = new CompletableFuture<>(); 56 | vertx.setTimer(1, l -> future.complete(null)); 57 | return future; 58 | }); 59 | 60 | CountDownLatch latch = new CountDownLatch(threadCount); 61 | long start = System.nanoTime(); 62 | for (int i = 0; i < threadCount; i++) { 63 | executorService.submit(() -> { 64 | try { 65 | do { 66 | randomSleep(); 67 | throttling.onEvent(addresses[ThreadLocalRandom.current().nextInt(addresses.length)]); 68 | } while (SECONDS.convert(System.nanoTime() - start, NANOSECONDS) < duration); 69 | } finally { 70 | latch.countDown(); 71 | } 72 | }); 73 | } 74 | latch.await(); 75 | 76 | assertWaitUntil(() -> { 77 | if (events.size() != addresses.length) { 78 | return false; 79 | } 80 | for (List nanoTimes : events.values()) { 81 | // must synchronize on synchronizedList traversal 82 | synchronized (nanoTimes) { 83 | Long previous = null; 84 | for (Long nanoTime : nanoTimes) { 85 | if (previous != null) { 86 | if (MILLISECONDS.convert(nanoTime - previous, NANOSECONDS) < 20) { 87 | return false; 88 | } 89 | } 90 | previous = nanoTime; 91 | } 92 | } 93 | } 94 | return true; 95 | }, 1000); 96 | } 97 | 98 | private void randomSleep() { 99 | try { 100 | MILLISECONDS.sleep(ThreadLocalRandom.current().nextLong(5)); 101 | } catch (InterruptedException e) { 102 | Thread.currentThread().interrupt(); 103 | } 104 | } 105 | 106 | @Override 107 | protected void tearDown() throws Exception { 108 | executorService.shutdown(); 109 | assertTrue(executorService.awaitTermination(5, SECONDS)); 110 | super.tearDown(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/ext/web/sstore/InfinispanClusteredSessionHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.web.sstore; 18 | 19 | import io.vertx.Lifecycle; 20 | import io.vertx.LoggingTestWatcher; 21 | import io.vertx.core.*; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 24 | import io.vertx.ext.web.it.sstore.ClusteredSessionHandlerTest; 25 | import org.junit.Rule; 26 | import org.junit.rules.TemporaryFolder; 27 | 28 | import java.util.List; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * @author Thomas Segismont 33 | */ 34 | public class InfinispanClusteredSessionHandlerTest extends ClusteredSessionHandlerTest { 35 | 36 | @Rule 37 | public LoggingTestWatcher watchman = new LoggingTestWatcher(); 38 | 39 | @Rule 40 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 41 | 42 | @Override 43 | public void setUp() throws Exception { 44 | System.setProperty("jgroups.file.location", temporaryFolder.newFolder().getAbsolutePath()); 45 | super.setUp(); 46 | } 47 | 48 | @Override 49 | protected Future clusteredVertx(VertxOptions options, ClusterManager clusterManager) { 50 | Future ret = super.clusteredVertx(options, clusterManager); 51 | try { 52 | ret.await(2, TimeUnit.MINUTES); 53 | } catch (Exception e) { 54 | fail(e.getMessage()); 55 | } 56 | return ret; 57 | } 58 | 59 | @Override 60 | protected ClusterManager getClusterManager() { 61 | return new InfinispanClusterManager(); 62 | } 63 | 64 | @Override 65 | protected void close(List clustered) throws Exception { 66 | Lifecycle.close(clustered); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/java/io/vertx/it/core/ServiceProviderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.it.core; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.VertxOptions; 21 | import io.vertx.core.internal.VertxInternal; 22 | import io.vertx.ext.cluster.infinispan.InfinispanClusterManager; 23 | import org.junit.Test; 24 | 25 | import java.util.concurrent.CompletableFuture; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | import static org.junit.Assert.assertTrue; 29 | 30 | public class ServiceProviderTest { 31 | 32 | @Test 33 | public void testDiscovery() throws Exception { 34 | CompletableFuture result = new CompletableFuture<>(); 35 | Vertx v1 = Vertx.clusteredVertx(new VertxOptions()).toCompletionStage().toCompletableFuture().get(30, TimeUnit.SECONDS); 36 | Vertx v2 = Vertx.clusteredVertx(new VertxOptions()).toCompletionStage().toCompletableFuture().get(30, TimeUnit.SECONDS); 37 | assertTrue(((VertxInternal) v1).clusterManager() instanceof InfinispanClusterManager); 38 | assertTrue(((VertxInternal) v2).clusterManager() instanceof InfinispanClusterManager); 39 | v1.eventBus().consumer("the-address", msg -> { 40 | if ("ping".equals(msg.body())) { 41 | result.complete(null); 42 | } else { 43 | result.completeExceptionally(new Exception()); 44 | } 45 | }).completion().onComplete(ar -> { 46 | if (ar.succeeded()) { 47 | v2.eventBus().send("the-address", "ping"); 48 | } else { 49 | result.completeExceptionally(ar.cause()); 50 | } 51 | }); 52 | result.get(30, TimeUnit.SECONDS); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/resources/jgroups.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 32 | 35 | 38 | 39 | 40 | 44 | 45 | 52 | 58 | 61 | 64 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /vertx-infinispan/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | %d [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 4.0.0 22 | 23 | 24 | io.vertx 25 | vertx-infinispan-parent 26 | 5.1.0-SNAPSHOT 27 | 28 | 29 | vertx-web-sstore-infinispan 30 | 31 | Vert.x Web Session Store with Infinispan 32 | 33 | 34 | 35 | io.vertx 36 | vertx-web 37 | 38 | 39 | io.vertx 40 | vertx-codegen-api 41 | true 42 | 43 | 44 | io.vertx 45 | vertx-codegen-json 46 | true 47 | 48 | 49 | io.vertx 50 | vertx-docgen-api 51 | true 52 | 53 | 54 | org.infinispan 55 | infinispan-client-hotrod-jakarta 56 | ${infinispan.version} 57 | 58 | 59 | io.reactivex.rxjava3 60 | rxjava 61 | 62 | 63 | org.wildfly.common 64 | wildfly-common 65 | 66 | 67 | caffeine 68 | com.github.ben-manes.caffeine 69 | 70 | 71 | 72 | 73 | org.wildfly.common 74 | wildfly-common 75 | 1.5.1.Final 76 | 77 | 78 | 79 | com.github.ben-manes.caffeine 80 | caffeine 81 | 2.8.0 82 | 83 | 84 | 85 | org.checkerframework 86 | checker-qual 87 | 88 | 89 | 90 | com.google.errorprone 91 | error_prone_annotations 92 | 93 | 94 | 95 | 96 | 97 | io.reactivex.rxjava3 98 | rxjava 99 | ${rxjava3.version} 100 | 101 | 102 | io.reactivestreams 103 | reactive-streams 104 | 105 | 106 | 107 | 108 | org.reactivestreams 109 | reactive-streams 110 | 1.0.3 111 | 112 | 113 | 114 | junit 115 | junit 116 | 4.13.1 117 | test 118 | 119 | 120 | io.vertx 121 | vertx-core 122 | test-jar 123 | test 124 | 125 | 126 | org.testcontainers 127 | testcontainers 128 | 1.15.2 129 | test 130 | 131 | 132 | io.vertx 133 | vertx-web 134 | test-jar 135 | test 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/asciidoc/web-sstore-infinispan.adoc: -------------------------------------------------------------------------------- 1 | = Infinispan Web Session Store 2 | 3 | An implementation of {@link io.vertx.ext.web.sstore.SessionStore} that relies on the https://infinispan.org/[Infinispan] Java Client. 4 | 5 | == Getting started 6 | 7 | To use this module, add the following to the _dependencies_ section of your Maven POM file: 8 | 9 | [source,xml,subs="+attributes"] 10 | ---- 11 | 12 | ${maven.groupId} 13 | ${maven.artifactId} 14 | ${maven.version} 15 | 16 | ---- 17 | 18 | Or, if you use Gradle: 19 | 20 | [source,groovy,subs="+attributes"] 21 | ---- 22 | compile '${maven.groupId}:${maven.artifactId}:${maven.version}' 23 | ---- 24 | 25 | == Using 26 | 27 | If this session store is the only one you have in your dependencies, you can initialize it in a generic way: 28 | 29 | [source,$lang] 30 | ---- 31 | {@link examples.Examples#simpleSetup} 32 | ---- 33 | 34 | Otherwise, use the {@link io.vertx.ext.web.sstore.infinispan.InfinispanSessionStore} type explicitely: 35 | 36 | [source,$lang] 37 | ---- 38 | {@link examples.Examples#explicitSetup} 39 | ---- 40 | 41 | == Configuring 42 | 43 | === Config entries 44 | 45 | The root entries are: 46 | 47 | * `servers`: mandatory, a JSON array of server definitions (see below) 48 | * `cacheName`: optional, the name of the cache used to store session data (defaults to `vertx-web.sessions`) 49 | * `retryTimeout`: optional, the retry timeout value in milli-seconds used by the session handler when it retrieves a value from the store (defaults to `5000`) 50 | 51 | The entries for a server definition are: 52 | 53 | * `uri` : optional, a https://infinispan.org/blog/2020/05/26/hotrod-uri/[Hot Rod URI] 54 | * `host`: optional (defaults to `localhost`) 55 | * `port`: optional (defaults to `11222`) 56 | * `clientIntelligence`: optional (one of `BASIC`, `TOPOLOGY_AWARE`, `HASH_DISTRIBUTION_AWARE`) 57 | * `username`: mandatory 58 | * `password`: mandatory 59 | * `realm`: optional (defaults to `default`) 60 | * `saslMechanism`: optional (defaults to `DIGEST-MD5`) 61 | * `saslQop`: optional (one of `AUTH`, `AUTH_INT`, `AUTH_CONF`) 62 | 63 | IMPORTANT: If the `uri` entry is set, the others are ignored. 64 | 65 | === Custom Infinispan Client 66 | 67 | For advanced configuration requirements, you can provide a custom https://docs.jboss.org/infinispan/12.1/apidocs/org/infinispan/client/hotrod/RemoteCacheManager.html[`RemoteCacheManager`]: 68 | 69 | [source,$lang] 70 | ---- 71 | {@link examples.Examples#customClient} 72 | ---- 73 | 74 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/java/examples/Examples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package examples; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.json.JsonArray; 21 | import io.vertx.core.json.JsonObject; 22 | import io.vertx.ext.web.Router; 23 | import io.vertx.ext.web.handler.SessionHandler; 24 | import io.vertx.ext.web.sstore.SessionStore; 25 | import io.vertx.ext.web.sstore.infinispan.InfinispanSessionStore; 26 | import org.infinispan.client.hotrod.RemoteCacheManager; 27 | 28 | @SuppressWarnings("unused") 29 | public class Examples { 30 | 31 | void simpleSetup(Vertx vertx, Router router) { 32 | JsonObject config = new JsonObject() 33 | .put("servers", new JsonArray() 34 | .add(new JsonObject() 35 | .put("host", "server1.datagrid.mycorp.int") 36 | .put("username", "foo") 37 | .put("password", "bar")) 38 | .add(new JsonObject() 39 | .put("host", "server2.datagrid.mycorp.int") 40 | .put("username", "foo") 41 | .put("password", "bar")) 42 | ); 43 | SessionStore store = SessionStore.create(vertx, config); 44 | SessionHandler sessionHandler = SessionHandler.create(store); 45 | router.route().handler(sessionHandler); 46 | } 47 | 48 | void explicitSetup(Vertx vertx, Router router) { 49 | JsonObject config = new JsonObject() 50 | .put("servers", new JsonArray() 51 | .add(new JsonObject() 52 | .put("host", "server1.datagrid.mycorp.int") 53 | .put("username", "foo") 54 | .put("password", "bar")) 55 | .add(new JsonObject() 56 | .put("host", "server2.datagrid.mycorp.int") 57 | .put("username", "foo") 58 | .put("password", "bar")) 59 | ); 60 | InfinispanSessionStore store = InfinispanSessionStore.create(vertx, config); 61 | SessionHandler sessionHandler = SessionHandler.create(store); 62 | router.route().handler(sessionHandler); 63 | } 64 | 65 | void customClient(Vertx vertx, JsonObject config, RemoteCacheManager remoteCacheManager) { 66 | InfinispanSessionStore sessionStore = InfinispanSessionStore.create(vertx, config, remoteCacheManager); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/java/examples/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | @Source 18 | package examples; 19 | 20 | import io.vertx.docgen.Source; 21 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/java/io/vertx/ext/web/sstore/infinispan/InfinispanSessionStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.web.sstore.infinispan; 18 | 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.json.JsonObject; 23 | import io.vertx.ext.web.sstore.SessionStore; 24 | import io.vertx.ext.web.sstore.infinispan.impl.InfinispanSessionStoreImpl; 25 | import org.infinispan.client.hotrod.RemoteCacheManager; 26 | 27 | import java.util.Objects; 28 | 29 | import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE; 30 | 31 | /** 32 | * An implementation of {@link SessionStore} that relies on the Infinispan Java Client. 33 | */ 34 | @VertxGen 35 | public interface InfinispanSessionStore extends SessionStore { 36 | 37 | /** 38 | * Create a new {@link InfinispanSessionStore} for the given configuration. 39 | * 40 | * @param vertx vertx instance 41 | * @param options the configuration 42 | * @return the new instance 43 | */ 44 | static InfinispanSessionStore create(Vertx vertx, JsonObject options) { 45 | InfinispanSessionStoreImpl store = new InfinispanSessionStoreImpl(); 46 | store.init(vertx, options); 47 | return store; 48 | } 49 | 50 | /** 51 | * Like {@link #create(Vertx, JsonObject)} but with a pre-configured Infinispan Client. 52 | * 53 | * @param vertx vertx instance 54 | * @param options the configuration 55 | * @param remoteCacheManager pre-configured Infinispan Client 56 | * @return the new instance 57 | */ 58 | @GenIgnore(PERMITTED_TYPE) 59 | static InfinispanSessionStore create(Vertx vertx, JsonObject options, RemoteCacheManager remoteCacheManager) { 60 | InfinispanSessionStoreImpl store = new InfinispanSessionStoreImpl(); 61 | store.init(vertx, options, Objects.requireNonNull(remoteCacheManager, "remoteCacheManager is required")); 62 | return store; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/java/io/vertx/ext/web/sstore/infinispan/impl/InfinispanSessionStoreImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.web.sstore.infinispan.impl; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.core.internal.VertxInternal; 23 | import io.vertx.core.json.JsonArray; 24 | import io.vertx.core.json.JsonObject; 25 | import io.vertx.ext.auth.prng.VertxContextPRNG; 26 | import io.vertx.ext.web.Session; 27 | import io.vertx.ext.web.sstore.AbstractSession; 28 | import io.vertx.ext.web.sstore.SessionStore; 29 | import io.vertx.ext.web.sstore.impl.SharedDataSessionImpl; 30 | import io.vertx.ext.web.sstore.infinispan.InfinispanSessionStore; 31 | import org.infinispan.client.hotrod.DefaultTemplate; 32 | import org.infinispan.client.hotrod.RemoteCache; 33 | import org.infinispan.client.hotrod.RemoteCacheManager; 34 | import org.infinispan.client.hotrod.configuration.ClientIntelligence; 35 | import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; 36 | import org.infinispan.client.hotrod.configuration.SaslQop; 37 | import org.infinispan.client.hotrod.configuration.ServerConfigurationBuilder; 38 | import org.infinispan.commons.api.CacheContainerAdmin; 39 | 40 | import java.util.Objects; 41 | import java.util.concurrent.CompletableFuture; 42 | 43 | import static io.vertx.ext.web.sstore.ClusteredSessionStore.DEFAULT_RETRY_TIMEOUT; 44 | import static io.vertx.ext.web.sstore.ClusteredSessionStore.DEFAULT_SESSION_MAP_NAME; 45 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 46 | import static org.infinispan.client.hotrod.impl.ConfigurationProperties.DEFAULT_HOTROD_PORT; 47 | 48 | public class InfinispanSessionStoreImpl implements InfinispanSessionStore { 49 | 50 | private VertxInternal vertx; 51 | private JsonObject options; 52 | private VertxContextPRNG random; 53 | private RemoteCacheManager remoteCacheManager; 54 | private RemoteCache sessions; 55 | 56 | public InfinispanSessionStoreImpl() { 57 | // Required by service loader 58 | } 59 | 60 | @Override 61 | public SessionStore init(Vertx vertx, JsonObject options) { 62 | return init(vertx, options, null); 63 | } 64 | 65 | public SessionStore init(Vertx vertx, JsonObject options, RemoteCacheManager remoteCacheManager) { 66 | this.vertx = (VertxInternal) vertx; 67 | this.options = Objects.requireNonNull(options, "options are required"); 68 | random = VertxContextPRNG.current(vertx); 69 | if (remoteCacheManager != null) { 70 | this.remoteCacheManager = remoteCacheManager; 71 | } else { 72 | ConfigurationBuilder builder = new ConfigurationBuilder(); 73 | JsonArray servers = Objects.requireNonNull(options.getJsonArray("servers"), "servers list is required"); 74 | for (Object object : servers) { 75 | if (object instanceof JsonObject) { 76 | JsonObject server = (JsonObject) object; 77 | configure(builder.addServer(), server); 78 | } 79 | } 80 | this.remoteCacheManager = new RemoteCacheManager(builder.build()); 81 | } 82 | String cacheName = options.getString("cacheName", DEFAULT_SESSION_MAP_NAME); 83 | sessions = this.remoteCacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE) 84 | .getOrCreateCache(cacheName, DefaultTemplate.DIST_SYNC); 85 | return this; 86 | } 87 | 88 | private static void configure(ServerConfigurationBuilder builder, JsonObject server) { 89 | String uri = server.getString("uri"); 90 | if (uri != null) { 91 | builder.uri(uri); 92 | } else { 93 | builder 94 | .host(server.getString("host", "localhost")) 95 | .port(server.getInteger("port", DEFAULT_HOTROD_PORT)); 96 | String clientIntelligence = server.getString("clientIntelligence"); 97 | if (clientIntelligence != null) { 98 | builder.clientIntelligence(ClientIntelligence.valueOf(clientIntelligence)); 99 | } 100 | builder.security().authentication() 101 | .username(Objects.requireNonNull(server.getString("username"), "username is required")) 102 | .password(Objects.requireNonNull(server.getString("password"), "password is required")) 103 | .realm(server.getString("realm", "default")) 104 | .saslMechanism(server.getString("saslMechanism", "DIGEST-MD5")); 105 | String saslQop = server.getString("saslQop"); 106 | if (saslQop != null) { 107 | builder.security().authentication() 108 | .saslQop(SaslQop.valueOf(saslQop)); 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public long retryTimeout() { 115 | return options.getLong("retryTimeout", DEFAULT_RETRY_TIMEOUT); 116 | } 117 | 118 | @Override 119 | public Session createSession(long timeout) { 120 | return createSession(timeout, DEFAULT_SESSIONID_LENGTH); 121 | } 122 | 123 | @Override 124 | public Session createSession(long timeout, int length) { 125 | return new SharedDataSessionImpl(random, timeout, length); 126 | } 127 | 128 | @Override 129 | public Future get(String id) { 130 | return Future.fromCompletionStage(sessions.getAsync(id), vertx.getOrCreateContext()).map(current -> { 131 | SharedDataSessionImpl session; 132 | if (current == null) { 133 | session = null; 134 | } else { 135 | session = new SharedDataSessionImpl(random); 136 | session.readFromBuffer(0, Buffer.buffer(current)); 137 | } 138 | return session; 139 | }); 140 | } 141 | 142 | @Override 143 | public Future delete(String id) { 144 | return Future.fromCompletionStage(sessions.removeAsync(id), vertx.getOrCreateContext()).mapEmpty(); 145 | } 146 | 147 | @Override 148 | public Future put(Session session) { 149 | return Future.fromCompletionStage(sessions.getAsync(session.id()), vertx.getOrCreateContext()).compose(current -> { 150 | AbstractSession newSession = (AbstractSession) session; 151 | if (current != null) { 152 | // Old session exists, we need to validate versions 153 | SharedDataSessionImpl oldSession = new SharedDataSessionImpl(random); 154 | oldSession.readFromBuffer(0, Buffer.buffer(current)); 155 | 156 | if (oldSession.version() != newSession.version()) { 157 | return Future.failedFuture("Session version mismatch"); 158 | } 159 | } 160 | 161 | newSession.incrementVersion(); 162 | return writeSession(newSession); 163 | }); 164 | } 165 | 166 | private Future writeSession(Session session) { 167 | Buffer buffer = Buffer.buffer(); 168 | SharedDataSessionImpl sessionImpl = (SharedDataSessionImpl) session; 169 | sessionImpl.writeToBuffer(buffer); 170 | 171 | CompletableFuture putAsync = sessions.putAsync(session.id(), buffer.getBytes(), 2 * session.timeout(), MILLISECONDS, session.timeout(), MILLISECONDS); 172 | return Future.fromCompletionStage(putAsync, vertx.getOrCreateContext()).mapEmpty(); 173 | } 174 | 175 | @Override 176 | public Future clear() { 177 | return Future.fromCompletionStage(sessions.clearAsync(), vertx.getOrCreateContext()); 178 | } 179 | 180 | @Override 181 | public Future size() { 182 | return Future.fromCompletionStage(sessions.sizeAsync(), vertx.getOrCreateContext()) 183 | .map(val -> (int) Math.min(val, Integer.MAX_VALUE)); 184 | } 185 | 186 | @Override 187 | public void close() { 188 | remoteCacheManager.close(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/java/io/vertx/ext/web/sstore/infinispan/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | @ModuleGen(name = "vertx-web-sstore-infinispan", groupPackage = "io.vertx") 18 | package io.vertx.ext.web.sstore.infinispan; 19 | 20 | import io.vertx.codegen.annotations.ModuleGen; 21 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/main/resources/META-INF/services/io.vertx.ext.web.sstore.SessionStore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 Red Hat, Inc. 3 | # 4 | # Red Hat licenses this file to you under the Apache License, version 2.0 5 | # (the "License"); you may not use this file except in compliance with the 6 | # License. You may obtain a copy of the License at: 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | # 16 | 17 | io.vertx.ext.web.sstore.infinispan.impl.InfinispanSessionStoreImpl 18 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/test/java/io/vertx/ext/web/sstore/infinispan/impl/InfinispanSessionHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.web.sstore.infinispan.impl; 18 | 19 | import io.vertx.core.json.JsonArray; 20 | import io.vertx.core.json.JsonObject; 21 | import io.vertx.ext.web.tests.handler.SessionHandlerTestBase; 22 | import io.vertx.ext.web.sstore.infinispan.InfinispanSessionStore; 23 | import org.junit.ClassRule; 24 | import org.testcontainers.containers.BindMode; 25 | import org.testcontainers.containers.GenericContainer; 26 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; 27 | 28 | import static org.infinispan.client.hotrod.impl.ConfigurationProperties.DEFAULT_HOTROD_PORT; 29 | 30 | public class InfinispanSessionHandlerTest extends SessionHandlerTestBase { 31 | 32 | private static final String IDENTITIES_BATCH = "/user-config/identities.batch"; 33 | private static final String USER = "foo"; 34 | private static final String PASS = "bar"; 35 | 36 | @ClassRule 37 | public static GenericContainer container = 38 | new GenericContainer<>("infinispan/server:13.0.10.Final") 39 | .withExposedPorts(DEFAULT_HOTROD_PORT) 40 | .withClasspathResourceMapping("identities.batch", "/user-config/identities.batch", BindMode.READ_ONLY) 41 | .withEnv("IDENTITIES_BATCH", IDENTITIES_BATCH) 42 | .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Infinispan Server.*started in.*\\s")); 43 | 44 | @Override 45 | public void setUp() throws Exception { 46 | super.setUp(); 47 | JsonObject config = new JsonObject() 48 | .put("servers", new JsonArray().add(new JsonObject() 49 | .put("host", container.getHost()) 50 | .put("port", container.getMappedPort(DEFAULT_HOTROD_PORT)) 51 | .put("username", USER) 52 | .put("password", PASS) 53 | )); 54 | store = InfinispanSessionStore.create(vertx, config); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /vertx-web-sstore-infinispan/src/test/resources/identities.batch: -------------------------------------------------------------------------------- 1 | user create "foo" -p "bar" -g admin 2 | --------------------------------------------------------------------------------