├── .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