├── src
├── main
│ ├── resources
│ │ ├── META-INF
│ │ │ └── services
│ │ │ │ └── io.vertx.core.spi.VertxServiceProvider
│ │ └── default-zookeeper.json
│ ├── java
│ │ ├── example
│ │ │ ├── package-info.java
│ │ │ └── Examples.java
│ │ └── io
│ │ │ └── vertx
│ │ │ └── spi
│ │ │ └── cluster
│ │ │ └── zookeeper
│ │ │ ├── package-info.java
│ │ │ ├── impl
│ │ │ ├── ZKLock.java
│ │ │ ├── RetryPolicyHelper.java
│ │ │ ├── ConfigUtil.java
│ │ │ ├── ZKCounter.java
│ │ │ ├── ZKSyncMap.java
│ │ │ ├── SubsMapHelper.java
│ │ │ ├── ZKAsyncMap.java
│ │ │ └── ZKMap.java
│ │ │ └── ZookeeperClusterManager.java
│ └── asciidoc
│ │ └── index.adoc
└── test
│ ├── resources
│ ├── zookeeper.json
│ └── log4j.properties
│ └── java
│ └── io
│ └── vertx
│ ├── spi
│ └── cluster
│ │ └── zookeeper
│ │ ├── RetryPolicyTest.java
│ │ ├── ZKSyncMapTest.java
│ │ ├── MockZKCluster.java
│ │ └── ConsumerRoundRobinTest.java
│ ├── core
│ ├── ZKClusteredHATest.java
│ ├── eventbus
│ │ ├── ZKNodeInfoTest.java
│ │ ├── ZKFaultToleranceTest.java
│ │ └── ZKClusteredEventbusTest.java
│ ├── ZKClusteredComplexHATest.java
│ ├── shareddata
│ │ ├── ZKClusteredAsynchronousLockTest.java
│ │ ├── ZKClusteredSharedCounterTest.java
│ │ └── ZKClusteredAsyncMapTest.java
│ └── ProgrammaticZKClusterManagerTest.java
│ ├── ext
│ └── web
│ │ └── sstore
│ │ └── ZKClusteredSessionHandlerTest.java
│ └── servicediscovery
│ └── impl
│ └── ZKDiscoveryImplClusteredTest.java
├── .editorconfig
├── .gitignore
├── .travis.deploy.artifacts.sh
├── .github
├── workflows
│ ├── ci-5.x.yml
│ ├── ci-5.x-stable.yml
│ ├── ci-4.x.yml
│ ├── ci.yml
│ ├── ci-matrix-5.x.yml
│ └── deploy.yml
├── maven-ci-settings.xml
└── maven-cd-settings.xml
├── README.adoc
├── LICENSE.txt
├── .travis.yml
└── pom.xml
/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider:
--------------------------------------------------------------------------------
1 | io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/test/resources/zookeeper.json:
--------------------------------------------------------------------------------
1 | {
2 | "zookeeperHosts":"127.0.0.1",
3 | "sessionTimeout":5000,
4 | "connectTimeout":3000,
5 | "rootPath":"io.vertx",
6 | "retry": {
7 | "initialSleepTime":100,
8 | "intervalTimes":10000,
9 | "maxTimes":5
10 | }
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | .idea
4 | .vertx
5 | .classpath
6 | .project
7 | .settings
8 | .yardoc
9 | .yardopts
10 | build
11 | target
12 | out
13 | *.iml
14 | *.ipr
15 | *.iws
16 | test-output
17 | Scratch.java
18 | ScratchTest.java
19 | test-results
20 | test-tmp
21 | *.class
22 |
--------------------------------------------------------------------------------
/src/main/resources/default-zookeeper.json:
--------------------------------------------------------------------------------
1 | {
2 | "zookeeperHosts":"127.0.0.1",
3 | "sessionTimeout":20000,
4 | "connectTimeout":3000,
5 | "rootPath":"io.vertx",
6 | "retry": {
7 | "policy": "exponential_backoff",
8 | "initialSleepTime":100,
9 | "intervalTimes":10000,
10 | "maxTimes":5
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.travis.deploy.artifacts.sh:
--------------------------------------------------------------------------------
1 | PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -B | grep -v '\[')
2 | if [[ "$PROJECT_VERSION" =~ .*SNAPSHOT ]] && [[ "${TRAVIS_BRANCH}" =~ ^master$|^[0-9]+\.[0-9]+$ ]] && [[ "${TRAVIS_PULL_REQUEST}" = "false" ]];
3 | then
4 | mvn deploy -s .travis.maven.settings.xml -DskipTests -B;
5 | fi
6 |
--------------------------------------------------------------------------------
/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Set root logger level to DEBUG and its only appender to CONSOLE.
2 | log4j.rootLogger=INFO, CONSOLE
3 |
4 | # CONSOLE
5 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
6 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %C{1} : %m%n
8 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-zookeeper (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-5.x-stable.yml:
--------------------------------------------------------------------------------
1 | name: vertx-zookeeper (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 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Zookeeper Cluster Manager
2 |
3 | image:https://github.com/vert-x3/vertx-zookeeper/workflows/CI/badge.svg?branch=master["Build Status", link="https://github.com/vert-x3/vertx-zookeeper/actions?query=workflow%3ACI"]
4 |
5 | This is a cluster manager implementation for Vert.x that uses http://zookeeper.apache.org/[Zookeeper].
6 |
7 | Please see the main documentation on the web-site for a full description:
8 |
9 | * https://vertx.io/docs/vertx-zookeeper/java/[Web-site documentation]
10 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2013 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
--------------------------------------------------------------------------------
/.github/workflows/ci-4.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-zookeeper (4.x)
2 | on:
3 | schedule:
4 | - cron: '0 4 * * *'
5 | jobs:
6 | CI:
7 | strategy:
8 | matrix:
9 | include:
10 | - os: ubuntu-latest
11 | jdk: 8
12 | uses: ./.github/workflows/ci.yml
13 | with:
14 | branch: 4.x
15 | jdk: ${{ matrix.jdk }}
16 | os: ${{ matrix.os }}
17 | secrets: inherit
18 | Deploy:
19 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }}
20 | needs: CI
21 | uses: ./.github/workflows/deploy.yml
22 | with:
23 | branch: 4.x
24 | jdk: 8
25 | secrets: inherit
26 |
--------------------------------------------------------------------------------
/src/main/java/example/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2013 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | @Source
17 | package example;
18 |
19 | import io.vertx.docgen.Source;
--------------------------------------------------------------------------------
/.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 | jobs:
15 | Test:
16 | name: Run tests
17 | runs-on: ${{ inputs.os }}
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: Run tests
29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B
30 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2016 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.spi.cluster.zookeeper;
18 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | install: true
3 | branches:
4 | only:
5 | - master
6 | - /^\d+\.\d+$/
7 | cache:
8 | directories:
9 | - $HOME/.m2
10 | before_cache:
11 | - rm -rf $HOME/.m2/repository/io/vertx/
12 | jobs:
13 | include:
14 | - stage: test
15 | name: "Unit tests - OracleJDK 8"
16 | script: mvn -B -DtestLogLevel=OFF test
17 | jdk: oraclejdk8
18 | - name: "Unit tests - OpenJDK 11"
19 | if: type != pull_request
20 | script: mvn -B -DtestLogLevel=OFF test
21 | jdk: openjdk11
22 | - stage: deploy
23 | name: "Deploy to Sonatype's snapshots repository"
24 | jdk: openjdk8
25 | if: type != pull_request
26 | script: bash .travis.deploy.artifacts.sh
27 | notifications:
28 | email:
29 | recipients:
30 | - stream1984@me.com
31 | on_success: always
32 | on_failure: always
33 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/spi/cluster/zookeeper/RetryPolicyTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.spi.cluster.zookeeper.impl.RetryPolicyHelper;
5 | import org.apache.curator.RetryPolicy;
6 | import org.apache.curator.retry.ExponentialBackoffRetry;
7 | import org.apache.curator.retry.RetryOneTime;
8 | import org.junit.Assert;
9 | import org.junit.Test;
10 |
11 | public class RetryPolicyTest {
12 |
13 | @Test
14 | public void createDefaultRetryPolicy(){
15 | JsonObject config = new JsonObject();
16 | RetryPolicy policy = RetryPolicyHelper.createRetryPolicy(config);
17 | Assert.assertTrue(policy instanceof ExponentialBackoffRetry);
18 | }
19 |
20 | @Test
21 | public void createOneTimeRetryPolicy(){
22 | JsonObject config = new JsonObject().put("policy","one_time");
23 | RetryPolicy policy = RetryPolicyHelper.createRetryPolicy(config);
24 | Assert.assertTrue(policy instanceof RetryOneTime);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/ZKClusteredHATest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
21 |
22 | /**
23 | * Created by stream.Liu
24 | */
25 | public class ZKClusteredHATest extends io.vertx.tests.ha.HATest {
26 |
27 | private MockZKCluster zkClustered = new MockZKCluster();
28 |
29 | public void after() throws Exception {
30 | super.after();
31 | zkClustered.stop();
32 | }
33 |
34 | @Override
35 | protected ClusterManager getClusterManager() {
36 | return zkClustered.getClusterManager();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/eventbus/ZKNodeInfoTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.core.eventbus;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
21 |
22 | public class ZKNodeInfoTest extends io.vertx.tests.eventbus.NodeInfoTest {
23 |
24 | private MockZKCluster zkClustered = new MockZKCluster();
25 |
26 | @Override
27 | protected void tearDown() throws Exception {
28 | super.tearDown();
29 | zkClustered.stop();
30 | }
31 |
32 | @Override
33 | protected ClusterManager getClusterManager() {
34 | return zkClustered.getClusterManager();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/ZKClusteredComplexHATest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
21 |
22 | /**
23 | * Created by stream.Liu
24 | */
25 | public class ZKClusteredComplexHATest extends io.vertx.tests.ha.ComplexHATest {
26 |
27 | private MockZKCluster zkClustered = new MockZKCluster();
28 |
29 | public void after() throws Exception {
30 | super.after();
31 | zkClustered.stop();
32 | }
33 |
34 | @Override
35 | protected ClusterManager getClusterManager() {
36 | return zkClustered.getClusterManager();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/shareddata/ZKClusteredAsynchronousLockTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core.shareddata;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
21 |
22 | /**
23 | * Created by stream.Liu
24 | */
25 | public class ZKClusteredAsynchronousLockTest extends io.vertx.tests.shareddata.ClusteredAsynchronousLockTest {
26 |
27 | private MockZKCluster zkClustered = new MockZKCluster();
28 |
29 | public void after() throws Exception {
30 | super.after();
31 | zkClustered.stop();
32 | }
33 |
34 | @Override
35 | protected ClusterManager getClusterManager() {
36 | return zkClustered.getClusterManager();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/shareddata/ZKClusteredSharedCounterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core.shareddata;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
21 |
22 | import java.util.concurrent.TimeUnit;
23 |
24 | /**
25 | *
26 | */
27 | public class ZKClusteredSharedCounterTest extends io.vertx.tests.shareddata.ClusteredSharedCounterTest {
28 |
29 | private MockZKCluster zkClustered = new MockZKCluster();
30 |
31 | public void await(long delay, TimeUnit timeUnit) {
32 | super.await(15, TimeUnit.SECONDS);
33 | }
34 |
35 | public void after() throws Exception {
36 | super.after();
37 | zkClustered.stop();
38 | }
39 |
40 | @Override
41 | protected ClusterManager getClusterManager() {
42 | return zkClustered.getClusterManager();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/web/sstore/ZKClusteredSessionHandlerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.ext.web.sstore;
18 |
19 | import io.vertx.core.spi.cluster.ClusterManager;
20 | import io.vertx.ext.web.it.sstore.ClusteredSessionHandlerTest;
21 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
22 | import org.junit.Ignore;
23 | import org.junit.Test;
24 |
25 | /**
26 | * @author Thomas Segismont
27 | */
28 | public class ZKClusteredSessionHandlerTest extends ClusteredSessionHandlerTest {
29 |
30 | private MockZKCluster zkClustered = new MockZKCluster();
31 |
32 | @Override
33 | public void tearDown() throws Exception {
34 | super.tearDown();
35 | zkClustered.stop();
36 | }
37 |
38 | @Override
39 | protected ClusterManager getClusterManager() {
40 | return zkClustered.getClusterManager();
41 | }
42 |
43 | @Ignore @Test
44 | public void testSessionExpires() {
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/example/Examples.java:
--------------------------------------------------------------------------------
1 | package example;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.VertxOptions;
5 | import io.vertx.core.json.JsonObject;
6 | import io.vertx.core.spi.cluster.ClusterManager;
7 | import io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager;
8 | import org.apache.curator.framework.CuratorFramework;
9 |
10 | /**
11 | * Created by stream.
12 | */
13 | public class Examples {
14 |
15 | public void example1() {
16 | ClusterManager mgr = new ZookeeperClusterManager();
17 | Vertx.builder()
18 | .withClusterManager(mgr)
19 | .buildClustered().onComplete(res -> {
20 | if (res.succeeded()) {
21 | Vertx vertx = res.result();
22 | } else {
23 | // failed!
24 | }
25 | });
26 | }
27 |
28 | public void example2() {
29 | JsonObject zkConfig = new JsonObject();
30 | zkConfig.put("zookeeperHosts", "127.0.0.1");
31 | zkConfig.put("rootPath", "io.vertx");
32 | zkConfig.put("retry", new JsonObject()
33 | .put("initialSleepTime", 3000)
34 | .put("maxTimes", 3));
35 |
36 |
37 | ClusterManager mgr = new ZookeeperClusterManager(zkConfig);
38 |
39 | Vertx.builder()
40 | .withClusterManager(mgr)
41 | .buildClustered().onComplete(res -> {
42 | if (res.succeeded()) {
43 | Vertx vertx = res.result();
44 | } else {
45 | // failed!
46 | }
47 | });
48 | }
49 |
50 | public void example3(CuratorFramework curator) {
51 | ClusterManager mgr = new ZookeeperClusterManager(curator);
52 | Vertx.builder().withClusterManager(mgr).buildClustered().onComplete(res -> {
53 | if (res.succeeded()) {
54 | Vertx vertx = res.result();
55 | } else {
56 | // failed!
57 | }
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/servicediscovery/impl/ZKDiscoveryImplClusteredTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.servicediscovery.impl;
18 |
19 | import io.vertx.core.Vertx;
20 | import io.vertx.core.VertxOptions;
21 | import io.vertx.servicediscovery.ServiceDiscoveryOptions;
22 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
23 | import org.junit.After;
24 | import org.junit.Before;
25 |
26 | import static com.jayway.awaitility.Awaitility.await;
27 |
28 | public class ZKDiscoveryImplClusteredTest extends DiscoveryImplTestBase {
29 |
30 | private MockZKCluster zkClustered = new MockZKCluster();
31 |
32 | @Before
33 | public void setUp() {
34 | VertxOptions options = new VertxOptions();
35 | options.getEventBusOptions().setHost("localhost").setPort(0);
36 | Vertx.builder().with(options).withClusterManager(zkClustered.getClusterManager()).buildClustered().onComplete(ar -> {
37 | vertx = ar.result();
38 | });
39 | await().until(() -> vertx != null);
40 | discovery = new DiscoveryImpl(vertx, new ServiceDiscoveryOptions());
41 | }
42 |
43 | @After
44 | public void tearDown() {
45 | super.tearDown();
46 | zkClustered.stop();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKLock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.spi.cluster.zookeeper.impl;
18 |
19 | import io.vertx.core.internal.logging.Logger;
20 | import io.vertx.core.internal.logging.LoggerFactory;
21 | import io.vertx.core.shareddata.Lock;
22 | import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
23 |
24 | import java.util.concurrent.ExecutorService;
25 |
26 | /**
27 | * Lock implementation.
28 | */
29 | public class ZKLock implements Lock {
30 |
31 | private static final Logger log = LoggerFactory.getLogger(ZKLock.class);
32 |
33 | private final InterProcessSemaphoreMutex lock;
34 | private final ExecutorService lockReleaseExec;
35 |
36 | public ZKLock(InterProcessSemaphoreMutex lock, ExecutorService lockReleaseExec) {
37 | this.lock = lock;
38 | this.lockReleaseExec = lockReleaseExec;
39 | }
40 |
41 | public InterProcessSemaphoreMutex getLock() {
42 | return lock;
43 | }
44 |
45 | @Override
46 | public void release() {
47 | lockReleaseExec.execute(() -> {
48 | try {
49 | lock.release();
50 | } catch (Exception e) {
51 | log.error(e);
52 | }
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/spi/cluster/zookeeper/ZKSyncMapTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper;
2 |
3 | import io.vertx.spi.cluster.zookeeper.impl.ZKSyncMap;
4 | import org.apache.curator.RetryPolicy;
5 | import org.apache.curator.framework.CuratorFramework;
6 | import org.apache.curator.framework.CuratorFrameworkFactory;
7 | import org.apache.curator.retry.ExponentialBackoffRetry;
8 | import org.apache.curator.test.TestingServer;
9 | import org.apache.curator.test.Timing;
10 | import org.junit.Test;
11 |
12 | import static org.junit.Assert.*;
13 |
14 | /**
15 | *
16 | */
17 | public class ZKSyncMapTest {
18 |
19 | @Test
20 | public void syncMapOperation() throws Exception {
21 | Timing timing = new Timing();
22 | TestingServer server = new TestingServer();
23 |
24 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(100, 3);
25 | CuratorFramework curator = CuratorFrameworkFactory.builder()
26 | .namespace("io.vertx")
27 | .sessionTimeoutMs(timing.session())
28 | .connectionTimeoutMs(timing.connection())
29 | .connectString(server.getConnectString())
30 | .retryPolicy(retryPolicy)
31 | .build();
32 | curator.start();
33 |
34 | String k = "myKey";
35 | String v = "myValue";
36 |
37 | ZKSyncMap syncMap = new ZKSyncMap<>(curator, "mapTest");
38 |
39 | syncMap.put(k, v);
40 | assertFalse(syncMap.isEmpty());
41 |
42 | assertEquals(syncMap.get(k), v);
43 |
44 | assertTrue(syncMap.size() > 0);
45 | assertTrue(syncMap.containsKey(k));
46 | assertTrue(syncMap.containsValue(v));
47 |
48 | assertTrue(syncMap.keySet().contains(k));
49 | assertTrue(syncMap.values().contains(v));
50 |
51 | syncMap.entrySet().forEach(entry -> {
52 | assertEquals(k, entry.getKey());
53 | assertEquals(v, entry.getValue());
54 | });
55 |
56 | String value = syncMap.remove(k);
57 | assertEquals(value, v);
58 | assertNull(syncMap.get(k));
59 |
60 | syncMap.clear();
61 | assertTrue(syncMap.isEmpty());
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/RetryPolicyHelper.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper.impl;
2 |
3 | import io.vertx.core.internal.logging.Logger;
4 | import io.vertx.core.internal.logging.LoggerFactory;
5 | import io.vertx.core.json.JsonObject;
6 | import org.apache.curator.RetryPolicy;
7 | import org.apache.curator.retry.*;
8 |
9 | public class RetryPolicyHelper {
10 | private static final String DEFAULT_RETRY_POLICY = "exponential_backoff";
11 | private static final Logger log = LoggerFactory.getLogger(RetryPolicyHelper.class);
12 |
13 |
14 | /**
15 | * creates a {@link RetryPolicy} based on a JsonObject configuration.
16 | * It falls back to {@link ExponentialBackoffRetry} if no valid policy is specified.
17 | *
18 | * @param conf the configuration object
19 | * @return RetryPolicy instance
20 | */
21 | public static RetryPolicy createRetryPolicy(JsonObject conf){
22 | String policy = conf.getString("policy", DEFAULT_RETRY_POLICY);
23 | int initialSleepTime = conf.getInteger("initialSleepTime", 1000);
24 | int maxTimes = conf.getInteger("maxTimes", 5);
25 | int intervalTimes = conf.getInteger("intervalTimes",10000);
26 |
27 | switch (policy){
28 | case "bounded_exponential_backoff": return new BoundedExponentialBackoffRetry(initialSleepTime,maxTimes,intervalTimes);
29 | case "one_time": return new RetryOneTime(intervalTimes);
30 | case "n_times": return new RetryNTimes(maxTimes, intervalTimes);
31 | case "forever": return new RetryForever(intervalTimes);
32 | case "until_elapsed": return new RetryUntilElapsed(maxTimes,intervalTimes);
33 | case DEFAULT_RETRY_POLICY: return new ExponentialBackoffRetry(initialSleepTime, maxTimes, intervalTimes);
34 | default: {
35 | log.warn(String.format("%s is not a valid policy, falling back to %s",policy, DEFAULT_RETRY_POLICY));
36 | return new ExponentialBackoffRetry(initialSleepTime, maxTimes, intervalTimes);
37 | }
38 | }
39 | }
40 |
41 | private RetryPolicyHelper() {
42 | //Utility class
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/spi/cluster/zookeeper/MockZKCluster.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.core.spi.cluster.ClusterManager;
5 | import org.apache.curator.framework.CuratorFramework;
6 | import org.apache.curator.test.InstanceSpec;
7 | import org.apache.curator.test.TestingServer;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by Stream.Liu
14 | */
15 | public class MockZKCluster {
16 | private final InstanceSpec spec = new InstanceSpec(null, -1, -1, -1, true, -1, 10000, 120);
17 | private TestingServer server;
18 | private final List clusterManagers = new ArrayList<>();
19 |
20 | static {
21 | System.setProperty("zookeeper.extendedTypesEnabled", "true");
22 | }
23 |
24 | public MockZKCluster() {
25 | try {
26 | server = new TestingServer(spec, true);
27 | } catch (Exception e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | public JsonObject getDefaultConfig() {
33 | JsonObject config = new JsonObject();
34 | config.put("zookeeperHosts", server.getConnectString());
35 | config.put("sessionTimeout", 10000);
36 | config.put("connectTimeout", 5000);
37 | config.put("rootPath", "io.vertx");
38 | config.put("retry", new JsonObject()
39 | .put("initialSleepTime", 500)
40 | .put("maxTimes", 2));
41 | return config;
42 | }
43 |
44 | public CuratorFramework curator;
45 |
46 | public void stop() {
47 | try {
48 | clusterManagers.clear();
49 | if (server == null) {
50 | server = new TestingServer(spec, false);
51 | }
52 | server.restart();
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | }
56 | }
57 |
58 | public ClusterManager getClusterManager() {
59 | if (server == null) {
60 | try {
61 | server = new TestingServer(spec, true);
62 | } catch (Exception e) {
63 | e.printStackTrace();
64 | }
65 | }
66 |
67 | ZookeeperClusterManager zookeeperClusterManager = new ZookeeperClusterManager(getDefaultConfig());
68 | clusterManagers.add(zookeeperClusterManager);
69 | return zookeeperClusterManager;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/spi/cluster/zookeeper/ConsumerRoundRobinTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.spi.cluster.ClusterManager;
5 | import io.vertx.test.core.VertxTestBase;
6 | import org.junit.Test;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 | import java.util.concurrent.CompletableFuture;
11 | import java.util.concurrent.CountDownLatch;
12 | import java.util.concurrent.atomic.AtomicInteger;
13 |
14 | /**
15 | * Created by stream.
16 | */
17 | public class ConsumerRoundRobinTest extends VertxTestBase {
18 |
19 | private static final String MESSAGE_ADDRESS = "consumerAddress";
20 |
21 | protected ClusterManager getClusterManager() {
22 | MockZKCluster zkCluster = new MockZKCluster();
23 | return zkCluster.getClusterManager();
24 | }
25 |
26 |
27 | private CompletableFuture addConsumer(int index) {
28 | CompletableFuture future = new CompletableFuture<>();
29 | vertices[0].eventBus().
30 | consumer(MESSAGE_ADDRESS, message -> message.reply(index)).
31 | completion().onComplete(event -> {
32 | if (event.succeeded()) {
33 | future.complete(null);
34 | } else {
35 | future.completeExceptionally(event.cause());
36 | }
37 | });
38 | return future;
39 | }
40 |
41 | @Override
42 | public void setUp() throws Exception {
43 | super.setUp();
44 | startNodes(1);
45 | CountDownLatch latch = new CountDownLatch(1);
46 | addConsumer(0).thenCompose(aVoid -> addConsumer(1)).thenCompose(aVoid -> addConsumer(2)).
47 | whenComplete((aVoid, throwable) -> {
48 | if (throwable != null) {
49 | fail(throwable);
50 | } else {
51 | latch.countDown();
52 | }
53 | });
54 | awaitLatch(latch);
55 | }
56 |
57 | @Test
58 | public void roundRobin() {
59 | AtomicInteger counter = new AtomicInteger(0);
60 | Set results = new HashSet<>();
61 | Vertx vertx = vertices[0];
62 | vertx.setPeriodic(500, aLong -> vertx.eventBus().request(MESSAGE_ADDRESS, "Hi").onComplete(message -> {
63 | if (message.failed()) {
64 | fail(message.cause());
65 | } else {
66 | Integer result = (Integer) message.result().body();
67 | results.add(result);
68 | if (counter.incrementAndGet() == 3) {
69 | assertEquals(results.size(), counter.get());
70 | testComplete();
71 | }
72 | }
73 | }));
74 | await();
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/eventbus/ZKFaultToleranceTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.core.eventbus;
18 |
19 | import io.vertx.core.Future;
20 | import io.vertx.core.Vertx;
21 | import io.vertx.core.VertxOptions;
22 | import io.vertx.core.buffer.Buffer;
23 | import io.vertx.core.json.JsonObject;
24 | import io.vertx.core.spi.cluster.ClusterManager;
25 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
26 | import org.junit.Assert;
27 | import org.junit.Ignore;
28 | import org.junit.Test;
29 |
30 | import java.util.Optional;
31 | import java.util.concurrent.CountDownLatch;
32 | import java.util.concurrent.TimeUnit;
33 | import java.util.stream.Stream;
34 |
35 | public class ZKFaultToleranceTest extends io.vertx.tests.eventbus.FaultToleranceTest {
36 |
37 | private MockZKCluster zkClustered = new MockZKCluster();
38 |
39 | protected void startNodes(int numNodes, VertxOptions options) {
40 | CountDownLatch latch = new CountDownLatch(numNodes);
41 | vertices = new Vertx[numNodes];
42 | for (int i = 0; i < numNodes; i++) {
43 | int index = i;
44 | options.getEventBusOptions().setHost("localhost").setPort(0);
45 | Future res = clusteredVertx(options);
46 | res.onComplete(ar -> {
47 | try {
48 | if (ar.failed()) {
49 | ar.cause().printStackTrace();
50 | }
51 | assertTrue("Failed to start node", ar.succeeded());
52 | Vertx vertx = ar.result();
53 | vertices[index] = vertx;
54 | //
55 | String classpath = System.getProperty("java.class.path");
56 | JsonObject zkClusterConfig = zkClustered.getDefaultConfig();
57 | Optional testPath = Stream.of(classpath.split(":")).filter(path -> path.contains("test-classes")).findFirst();
58 | Assert.assertTrue(testPath.isPresent());
59 | String zkConfigFileName = testPath.get() + "/zookeeper.json";
60 | vertx.fileSystem().deleteBlocking(zkConfigFileName);
61 | vertx.fileSystem().createFileBlocking(zkConfigFileName);
62 | vertx.fileSystem().writeFileBlocking(zkConfigFileName, Buffer.buffer(zkClusterConfig.encode()));
63 | } finally {
64 | latch.countDown();
65 | }
66 | });
67 | }
68 | try {
69 | Thread.sleep(1500L);
70 | } catch (InterruptedException e) {
71 | //
72 | }
73 | try {
74 | assertTrue(latch.await(2, TimeUnit.MINUTES));
75 | } catch (InterruptedException e) {
76 | fail(e.getMessage());
77 | }
78 | }
79 |
80 | @Ignore
81 | @Test
82 | public void testFaultTolerance() throws Exception {
83 |
84 | }
85 |
86 | @Override
87 | protected void tearDown() throws Exception {
88 | super.tearDown();
89 | zkClustered.stop();
90 | }
91 |
92 | @Override
93 | protected ClusterManager getClusterManager() {
94 | return zkClustered.getClusterManager();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/ConfigUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2023 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.spi.cluster.zookeeper.impl;
18 |
19 | import io.vertx.core.internal.logging.Logger;
20 | import io.vertx.core.internal.logging.LoggerFactory;
21 | import io.vertx.core.json.JsonObject;
22 |
23 | import java.io.BufferedInputStream;
24 | import java.io.BufferedReader;
25 | import java.io.File;
26 | import java.io.FileInputStream;
27 | import java.io.FileNotFoundException;
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.io.InputStreamReader;
31 |
32 | public class ConfigUtil {
33 |
34 | private static final Logger log = LoggerFactory.getLogger(ConfigUtil.class);
35 |
36 | private static final String DEFAULT_CONFIG_FILE = "default-zookeeper.json";
37 | private static final String CONFIG_FILE = "zookeeper.json";
38 | private static final String ZK_SYS_CONFIG_KEY = "vertx.zookeeper.config";
39 |
40 | public static JsonObject loadConfig(String resourceLocation) {
41 | JsonObject conf = null;
42 | try (
43 | InputStream is = getConfigStream(resourceLocation != null ? resourceLocation : System.getProperty(ZK_SYS_CONFIG_KEY));
44 | BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is)))
45 | ) {
46 | String line;
47 | StringBuilder sb = new StringBuilder();
48 | while ((line = reader.readLine()) != null) {
49 | sb.append(line);
50 | }
51 | conf = new JsonObject(sb.toString());
52 | } catch (IOException ex) {
53 | log.error("Failed to read config", ex);
54 | }
55 | return conf;
56 | }
57 |
58 | private static InputStream getConfigStream(String resourceLocation) {
59 | InputStream is = getConfigStreamFor(resourceLocation);
60 | if (is == null) {
61 | is = getConfigStreamFromClasspath(CONFIG_FILE, DEFAULT_CONFIG_FILE);
62 | }
63 | return is;
64 | }
65 |
66 | private static InputStream getConfigStreamFor(String resourceLocation) {
67 | InputStream is = null;
68 | if (resourceLocation != null) {
69 | if (resourceLocation.startsWith("classpath:")) {
70 | return getConfigStreamFromClasspath(resourceLocation.substring("classpath:".length()), CONFIG_FILE);
71 | }
72 | File cfgFile = new File(resourceLocation);
73 | if (cfgFile.exists()) {
74 | try {
75 | is = new FileInputStream(cfgFile);
76 | } catch (FileNotFoundException ex) {
77 | log.warn(String.format("Failed to open file '%s' defined in '%s'. Continuing classpath search for %s", resourceLocation, ZK_SYS_CONFIG_KEY, CONFIG_FILE));
78 | }
79 | }
80 | }
81 | return is;
82 | }
83 |
84 | private static InputStream getConfigStreamFromClasspath(String configFile, String defaultConfig) {
85 | InputStream is = null;
86 | ClassLoader ctxClsLoader = Thread.currentThread().getContextClassLoader();
87 | if (ctxClsLoader != null) {
88 | is = ctxClsLoader.getResourceAsStream(configFile);
89 | }
90 | if (is == null) {
91 | is = ConfigUtil.class.getClassLoader().getResourceAsStream(configFile);
92 | if (is == null) {
93 | is = ConfigUtil.class.getClassLoader().getResourceAsStream(defaultConfig);
94 | }
95 | }
96 | return is;
97 | }
98 |
99 | private ConfigUtil() {
100 | // Utility class
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/shareddata/ZKClusteredAsyncMapTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core.shareddata;
18 |
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.core.spi.cluster.ClusterManager;
21 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
22 | import org.junit.Ignore;
23 | import org.junit.Test;
24 |
25 | import java.util.Objects;
26 |
27 | /**
28 | *
29 | */
30 | public class ZKClusteredAsyncMapTest extends io.vertx.tests.shareddata.ClusteredAsyncMapTest {
31 |
32 | private MockZKCluster zkClustered = new MockZKCluster();
33 |
34 | public void after() throws Exception {
35 | super.after();
36 | zkClustered.stop();
37 | }
38 |
39 | @Test
40 | @Override
41 | public void testMapReplaceIfPresentTtl() {
42 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> {
43 | map.replaceIfPresent("key", "old", "new", 100)
44 | .onSuccess(b -> fail("operation should not be implemented"))
45 | .onFailure(t -> {
46 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException);
47 | complete();
48 | });
49 | }));
50 | await();
51 | }
52 |
53 | @Test
54 | @Override
55 | public void testMapReplaceIfPresentTtlWhenNotPresent() {
56 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> {
57 | map.replaceIfPresent("key", "old", "new", 100)
58 | .onSuccess(b -> fail("operation should not be implemented"))
59 | .onFailure(t -> {
60 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException);
61 | complete();
62 | });
63 | }));
64 | await();
65 | }
66 |
67 | @Test
68 | @Override
69 | public void testMapReplaceTtl() {
70 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> {
71 | map.replace("key", "new", 100)
72 | .onSuccess(b -> fail("operation should not be implemented"))
73 | .onFailure(t -> {
74 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException);
75 | complete();
76 | });
77 | }));
78 | await();
79 | }
80 |
81 | @Test
82 | @Override
83 | public void testMapReplaceTtlWithPreviousValue() {
84 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> {
85 | map.replace("key", "new", 100)
86 | .onSuccess(b -> fail("operation should not be implemented"))
87 | .onFailure(t -> {
88 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException);
89 | complete();
90 | });
91 | }));
92 | await();
93 | }
94 |
95 | @Override
96 | protected ClusterManager getClusterManager() {
97 | return zkClustered.getClusterManager();
98 | }
99 |
100 | @Test
101 | public void testStoreAndGetBuffer() {
102 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> {
103 | map.put("test", Buffer.buffer().appendString("Hello")).onComplete(onSuccess(putResult -> map.get("test").onComplete(onSuccess(myBuffer -> {
104 | assertEquals("Hello", myBuffer.toString());
105 | testComplete();
106 | }))));
107 | }));
108 | await();
109 | }
110 |
111 | @Ignore
112 | @Override
113 | public void testMapPutThenPutTtl() {
114 | // This test fails upstream, the test is doing:
115 |
116 | // 1. get the async map: foo
117 | // 2. store the value "molo" under the key "pipo"
118 | // 3. store the value "mili" under the key "pipo" with TTL 15
119 | // 4. get the async map: foo
120 | // N. every 15, check if key "pipo" is NULL <-- THIS NEVER HAPPENS
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKCounter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.spi.cluster.zookeeper.impl;
18 |
19 | import io.vertx.core.Future;
20 | import io.vertx.core.VertxException;
21 | import io.vertx.core.internal.VertxInternal;
22 | import io.vertx.core.shareddata.Counter;
23 | import org.apache.curator.RetryPolicy;
24 | import org.apache.curator.framework.CuratorFramework;
25 | import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
26 |
27 | public class ZKCounter implements Counter {
28 |
29 | private static final String ZK_PATH_COUNTERS = "/counters/";
30 |
31 | private final VertxInternal vertx;
32 | private final DistributedAtomicLong atomicLong;
33 |
34 | public ZKCounter(VertxInternal vertx, CuratorFramework curator, String nodeName, RetryPolicy retryPolicy) {
35 | this.vertx = vertx;
36 | String counterPath = ZK_PATH_COUNTERS + nodeName;
37 | this.atomicLong = new DistributedAtomicLong(curator, counterPath, retryPolicy);
38 | }
39 |
40 | @Override
41 | public Future get() {
42 | return vertx.executeBlocking(() -> {
43 | try {
44 | return atomicLong.get().preValue();
45 | } catch (Exception e) {
46 | throw new VertxException(e, true);
47 | }
48 | });
49 | }
50 |
51 | @Override
52 | public Future incrementAndGet() {
53 | return increment(true);
54 | }
55 |
56 | @Override
57 | public Future getAndIncrement() {
58 | return increment(false);
59 | }
60 |
61 | private Future increment(boolean post) {
62 | return vertx.executeBlocking(() -> {
63 | try {
64 | long returnValue = 0;
65 | if (atomicLong.get().succeeded()) returnValue = atomicLong.get().preValue();
66 | if (atomicLong.increment().succeeded()) {
67 | return post ? atomicLong.get().postValue() : returnValue;
68 | } else {
69 | throw new VertxException("increment value failed.", true);
70 | }
71 | } catch (Exception e) {
72 | throw new VertxException(e, true);
73 | }
74 | });
75 | }
76 |
77 | @Override
78 | public Future decrementAndGet() {
79 | return vertx.executeBlocking(() -> {
80 | try {
81 | if (atomicLong.decrement().succeeded()) {
82 | return atomicLong.get().postValue();
83 | } else {
84 | throw new VertxException("decrement value failed.", true);
85 | }
86 | } catch (Exception e) {
87 | throw new VertxException(e, true);
88 | }
89 | });
90 | }
91 |
92 | @Override
93 | public Future addAndGet(long value) {
94 | return add(value, true);
95 | }
96 |
97 | @Override
98 | public Future getAndAdd(long value) {
99 | return add(value, false);
100 | }
101 |
102 | private Future add(long value, boolean post) {
103 | return vertx.executeBlocking(() -> {
104 | try {
105 | long returnValue = 0;
106 | if (atomicLong.get().succeeded()) returnValue = atomicLong.get().preValue();
107 | if (atomicLong.add(value).succeeded()) {
108 | return post ? atomicLong.get().postValue() : returnValue;
109 | } else {
110 | throw new VertxException("add value failed.", true);
111 | }
112 | } catch (Exception e) {
113 | throw new VertxException(e, true);
114 | }
115 | });
116 | }
117 |
118 | @Override
119 | public Future compareAndSet(long expected, long value) {
120 | return vertx.executeBlocking(() -> {
121 | try {
122 | if (atomicLong.get().succeeded() && atomicLong.get().preValue() == 0) this.atomicLong.initialize(0L);
123 | return atomicLong.compareAndSet(expected, value).succeeded();
124 | } catch (Exception e) {
125 | throw new VertxException(e, true);
126 | }
127 | });
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Zookeeper Cluster Manager
2 |
3 | This is a cluster manager implementation for Vert.x that uses http://zookeeper.apache.org/[Zookeeper].
4 |
5 | It implements interfaces of vert.x cluster totally. So you can using it to instead of vertx-hazelcast if you want.
6 | This implementation is packaged inside:
7 |
8 | [source,xml,subs="+attributes"]
9 | ----
10 |
11 | io.vertx
12 | vertx-zookeeper
13 | ${maven.version}
14 |
15 | ----
16 |
17 | In Vert.x a cluster manager is used for various functions including:
18 |
19 | * Discovery and group membership of Vert.x nodes in a cluster
20 | * Maintaining cluster wide topic subscriber lists (so we know which nodes are interested in which event busaddresses)
21 | * Distributed Map support
22 | * Distributed Locks
23 | * Distributed Counters
24 |
25 | Cluster managersdo not* handle the event bus inter-node transport, this is done directly by Vert.x with TCP connections.
26 |
27 | == How to work
28 | We are using http://curator.apache.org/[Apache Curator] framework rather than zookeeper client directly, so
29 | we have a dependency for libraries used in Curator such as `guava`, `slf4j` and of course `zookeeper`.
30 |
31 | Since ZK using tree dictionary to store data, we can take root path as namespace default root path is `io.vertx` which in default-zookeeper.json.
32 | and there are another 5 sub path to record other information for functions in vert.x cluster manager, all you can change the path is `root path`.
33 |
34 | you can find all the vert.x node information in path of `/io.vertx/cluster/nodes/`,
35 | `/io.vertx/asyncMap/$name/` record all the `AsyncMap` you created with `io.vertx.core.shareddata.AsyncMap` interface.
36 | `/io.vertx/asyncMultiMap/$name/` record all the `AsyncMultiMap` you created with `io.vertx.core.spi.cluster.AsyncMultiMap` interface.
37 | `/io.vertx/locks/` record distributed Locks information.
38 | `/io.vertx/counters/` record distributed Count information.
39 |
40 | == Using this cluster manager
41 |
42 | If you are using Vert.x from the command line, the jar corresponding to this cluster manager (it will be named `vertx-zookeeper-${maven.version}`.jar`
43 | should be in the `lib` directory of the Vert.x installation.
44 |
45 | If you want clustering with this cluster manager in your Vert.x Maven or Gradle project then just add a dependency to
46 | the artifact: `io.vertx:vertx-zookeeper:${version}` in your project.
47 |
48 | If the jar is on your classpath as above then Vert.x will automatically detect this and use it as the cluster manager.
49 | Please make sure you don't have any other cluster managers on your classpath or Vert.x might
50 | choose the wrong one.
51 |
52 | You can also specify the cluster manager programmatically if you are embedding Vert.x by specifying it on the options
53 | when you are creating your Vert.x instance, for example:
54 |
55 | [source, $lang]
56 | ----
57 | {@link example.Examples#example1()}
58 | ----
59 |
60 | == Configuring this cluster manager
61 |
62 | Usually the cluster manager is configured by a file
63 | https://github.com/vert-x3/vertx-zookeeper/blob/master/src/main/resources/default-zookeeper.json[`default-zookeeper.json`]
64 | which is packaged inside the jar.
65 |
66 | If you want to override this configuration you can provide a file called `zookeeper.json` on your classpath and this
67 | will be used instead. If you want to embed the `zookeeper.json` file in a fat jar, it must be located at the root of the
68 | fat jar. If it's an external file, the*directory** containing the file must be added to the classpath. For
69 | example, if you are using the _launcher_ class from Vert.x, the classpath enhancement can be done as follows:
70 |
71 | [source,shell]
72 | ----
73 | # If the zookeeper.json is in the current directory:
74 | java -jar ... -cp . -cluster
75 | vertx run MyVerticle -cp . -cluster
76 |
77 | # If the zookeeper.json is in the conf directory
78 | java -jar ... -cp conf -cluster
79 | ----
80 |
81 | Another way to override the configuration is by providing the system property `vertx.zookeeper.conf` with a
82 | location:
83 |
84 | [source,shell]
85 | ----
86 | # Use a cluster configuration located in an external file
87 | java -Dvertx.zookeeper.config=./config/my-zookeeper-conf.json -jar ... -cluster
88 |
89 | # Or use a custom configuration from the classpath
90 | java -Dvertx.zookeeper.config=classpath:my/package/config/my-cluster-config.json -jar ... -cluster
91 | ----
92 |
93 | The `vertx.zookeeper.config` system property, when present, overrides any `zookeeper.json` from the classpath, but if
94 | loading
95 | from this system property fails, then loading falls back to either `zookeeper.json` or the Zookeeper default configuration.
96 |
97 | The configuration file is described in detail in `default-zookeeper.json`'s comment.
98 |
99 | You can also specify configuration programmatically if embedding:
100 |
101 | [source,java]
102 | ----
103 | {@link example.Examples#example2()}
104 | ----
105 |
106 | The retry policy can be specified in the json configuration as following:
107 |
108 | [source,json]
109 | ----
110 | {
111 | "retry":{
112 | "policy": "exponential_backoff"
113 | }
114 | }
115 | ----
116 |
117 | The possible value for the policy are:
118 |
119 | * `exponential_backoff` (default)
120 | * `bounded_exponential_backoff`
121 | * `one_time`
122 | * `n_times`
123 | * `forever`
124 | * `until_elapsed`
125 |
126 |
127 | IMPORTANT: You can also configure the zookeeper hosts using the `vertx.zookeeper.hosts` system property.
128 |
129 | === Enabling logging
130 |
131 | When trouble-shooting clustering issues with Zookeeper it's often useful to get some logging output from Zookeeper
132 | to see if it's forming a cluster properly. You can do this (when using the default JUL logging) by adding a file
133 | called `vertx-default-jul-logging.properties` on your classpath. This is a standard java.util.logging (JUL)
134 | configuration file. Inside it set:
135 |
136 | [source,properties]
137 | ----
138 | org.apache.zookeeper.level=INFO
139 | ----
140 |
141 | and also
142 |
143 | [source,properties]
144 | ----
145 | java.util.logging.ConsoleHandler.level=INFO
146 | java.util.logging.FileHandler.level=INFO
147 | ----
148 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/core/eventbus/ZKClusteredEventbusTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package io.vertx.core.eventbus;
18 |
19 | import io.vertx.core.AsyncResult;
20 | import io.vertx.core.Handler;
21 | import io.vertx.core.Vertx;
22 | import io.vertx.core.spi.cluster.ClusterManager;
23 | import io.vertx.spi.cluster.zookeeper.MockZKCluster;
24 | import io.vertx.test.core.TestUtils;
25 | import org.junit.Test;
26 |
27 | import java.util.Map;
28 | import java.util.concurrent.TimeUnit;
29 | import java.util.concurrent.atomic.AtomicInteger;
30 | import java.util.function.Consumer;
31 |
32 | /**
33 | *
34 | */
35 |
36 | public class ZKClusteredEventbusTest extends io.vertx.tests.eventbus.ClusteredEventBusTest {
37 |
38 | private final MockZKCluster zkClustered = new MockZKCluster();
39 |
40 | @Override
41 | public void tearDown() throws Exception {
42 | super.tearDown();
43 | zkClustered.stop();
44 | }
45 |
46 | public void await(long delay, TimeUnit timeUnit) {
47 | //fail fast if test blocking
48 | super.await(10, TimeUnit.SECONDS);
49 | }
50 |
51 | @Override
52 | protected void testSend(T val, R received, Consumer consumer, DeliveryOptions options) {
53 | if (vertices == null) {
54 | startNodes(2);
55 | }
56 |
57 | MessageConsumer reg = vertices[1].eventBus().consumer(ADDRESS1).handler((Message msg) -> {
58 | if (consumer == null) {
59 | assertTrue(msg.isSend());
60 | assertEquals(received, msg.body());
61 | if (options != null) {
62 | assertNotNull(msg.headers());
63 | int numHeaders = options.getHeaders() != null ? options.getHeaders().size() : 0;
64 | assertEquals(numHeaders, msg.headers().size());
65 | if (numHeaders != 0) {
66 | for (Map.Entry entry : options.getHeaders().entries()) {
67 | assertEquals(msg.headers().get(entry.getKey()), entry.getValue());
68 | }
69 | }
70 | }
71 | } else {
72 | consumer.accept(msg.body());
73 | }
74 | testComplete();
75 | });
76 | reg.completion().onComplete(ar -> {
77 | assertTrue(ar.succeeded());
78 | vertices[1].setTimer(200L, along -> {
79 | if (options == null) {
80 | vertices[0].eventBus().send(ADDRESS1, val);
81 | } else {
82 | vertices[0].eventBus().send(ADDRESS1, val, options);
83 | }
84 | });
85 | });
86 | await();
87 | }
88 |
89 | @Override
90 | protected void testReply(T val, R received, Consumer consumer, DeliveryOptions options) {
91 | if (vertices == null) {
92 | startNodes(2);
93 | }
94 | String str = TestUtils.randomUnicodeString(1000);
95 | MessageConsumer> reg = vertices[1].eventBus().consumer(ADDRESS1).handler(msg -> {
96 | assertEquals(str, msg.body());
97 | if (options == null) {
98 | msg.reply(val);
99 | } else {
100 | msg.reply(val, options);
101 | }
102 | });
103 | reg.completion().onComplete(ar -> {
104 | assertTrue(ar.succeeded());
105 | vertices[1].setTimer(200L, along -> {
106 | vertices[0].eventBus().request(ADDRESS1, str).onComplete(onSuccess((Message reply) -> {
107 | if (consumer == null) {
108 | assertTrue(reply.isSend());
109 | assertEquals(received, reply.body());
110 | if (options != null && options.getHeaders() != null) {
111 | assertNotNull(reply.headers());
112 | assertEquals(options.getHeaders().size(), reply.headers().size());
113 | for (Map.Entry entry : options.getHeaders().entries()) {
114 | assertEquals(reply.headers().get(entry.getKey()), entry.getValue());
115 | }
116 | }
117 | } else {
118 | consumer.accept(reply.body());
119 | }
120 | testComplete();
121 | }));
122 | });
123 | });
124 | await();
125 | }
126 |
127 | @Test
128 | public void testLocalHandlerClusteredPublish() throws Exception {
129 | startNodes(2);
130 | waitFor(2);
131 | vertices[1].eventBus().consumer(ADDRESS1, msg -> complete()).completion().onComplete(v1 -> {
132 | vertices[0].eventBus().localConsumer(ADDRESS1, msg -> complete()).completion().onComplete(v2 -> {
133 | vertices[1].setTimer(200L, aLong -> {
134 | vertices[0].eventBus().publish(ADDRESS1, "foo");
135 | });
136 | });
137 | });
138 | await();
139 | }
140 |
141 | @Override
142 | protected void testPublish(T val, Consumer consumer) {
143 | int numNodes = 3;
144 | startNodes(numNodes);
145 | AtomicInteger count = new AtomicInteger();
146 | class MyHandler implements Handler> {
147 | @Override
148 | public void handle(Message msg) {
149 | if (consumer == null) {
150 | assertFalse(msg.isSend());
151 | assertEquals(val, msg.body());
152 | } else {
153 | consumer.accept(msg.body());
154 | }
155 | if (count.incrementAndGet() == numNodes - 1) {
156 | testComplete();
157 | }
158 | }
159 | }
160 | AtomicInteger registerCount = new AtomicInteger(0);
161 | class MyRegisterHandler implements Handler> {
162 | @Override
163 | public void handle(AsyncResult ar) {
164 | assertTrue(ar.succeeded());
165 | if (registerCount.incrementAndGet() == 2) {
166 | vertices[0].setTimer(300L, h -> {
167 | vertices[0].eventBus().publish(ADDRESS1, val);
168 | });
169 | }
170 | }
171 | }
172 | MessageConsumer reg = vertices[2].eventBus().consumer(ADDRESS1).handler(new MyHandler());
173 | reg.completion().onComplete(new MyRegisterHandler());
174 | reg = vertices[1].eventBus().consumer(ADDRESS1).handler(new MyHandler());
175 | reg.completion().onComplete(new MyRegisterHandler());
176 | await();
177 | }
178 |
179 | @Override
180 | protected ClusterManager getClusterManager() {
181 | return zkClustered.getClusterManager();
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKSyncMap.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2016 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.spi.cluster.zookeeper.impl;
17 |
18 | import io.vertx.core.VertxException;
19 | import io.vertx.core.internal.logging.Logger;
20 | import io.vertx.core.internal.logging.LoggerFactory;
21 | import org.apache.curator.framework.CuratorFramework;
22 | import org.apache.curator.framework.api.CuratorEventType;
23 | import org.apache.curator.framework.imps.CuratorFrameworkState;
24 | import org.apache.zookeeper.CreateMode;
25 | import org.apache.zookeeper.KeeperException;
26 |
27 | import java.io.Serializable;
28 | import java.util.Collection;
29 | import java.util.HashMap;
30 | import java.util.Map;
31 | import java.util.Set;
32 | import java.util.concurrent.CountDownLatch;
33 | import java.util.concurrent.TimeUnit;
34 | import java.util.stream.Collectors;
35 |
36 | /**
37 | * Created by Stream.Liu
38 | */
39 | public class ZKSyncMap extends ZKMap implements Map {
40 |
41 | private static final Logger logger = LoggerFactory.getLogger(ZKSyncMap.class);
42 |
43 | public ZKSyncMap(CuratorFramework curator, String mapName) {
44 | super(curator, null, ZK_PATH_SYNC_MAP, mapName);
45 | }
46 |
47 | @Override
48 | public int size() {
49 | try {
50 | return curator.getChildren().forPath(mapPath).size();
51 | } catch (Exception e) {
52 | throw new VertxException(e);
53 | }
54 | }
55 |
56 | @Override
57 | public boolean isEmpty() {
58 | try {
59 | syncKeyPath(mapPath);
60 | return curator.getChildren().forPath(mapPath).isEmpty();
61 | } catch (Exception e) {
62 | throw new VertxException(e);
63 | }
64 | }
65 |
66 | @Override
67 | public boolean containsKey(Object key) {
68 | try {
69 | String keyPath = keyPath((K) key);
70 | syncKeyPath(keyPath);
71 | return curator.checkExists().forPath(keyPath) != null;
72 | } catch (Exception e) {
73 | throw new VertxException(e);
74 | }
75 | }
76 |
77 | @Override
78 | public boolean containsValue(Object value) {
79 | try {
80 | syncKeyPath(mapPath);
81 | return curator.getChildren().forPath(mapPath).stream().anyMatch(k -> {
82 | try {
83 | byte[] bytes = curator.getData().forPath(keyPath((K) k));
84 | KeyValue keyValue = asObject(bytes);
85 | return keyValue.getValue().equals(value);
86 | } catch (Exception ex) {
87 | throw new VertxException(ex);
88 | }
89 | });
90 | } catch (Exception e) {
91 | throw new VertxException(e);
92 | }
93 | }
94 |
95 | @Override
96 | public V get(Object key) {
97 | try {
98 | String keyPath = keyPath((K) key);
99 | syncKeyPath(keyPath);
100 | if (null == curator.checkExists().forPath(keyPath)) {
101 | return null;
102 | } else {
103 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath));
104 | return keyValue.getValue();
105 | }
106 | } catch (Exception e) {
107 | if (e instanceof VertxException && e.getCause() instanceof KeeperException.NoNodeException) {
108 | logger.warn("zookeeper node lost. " + e.getCause().getMessage());
109 | } else {
110 | throw new VertxException(e);
111 | }
112 | }
113 | return null;
114 | }
115 |
116 | @Override
117 | public V put(K key, V value) {
118 | try {
119 | String keyPath = keyPath(key);
120 | KeyValue keyValue = new KeyValue<>(key, value);
121 | byte[] valueBytes = asByte(keyValue);
122 | if (get(key) != null) {
123 | curator.setData().forPath(keyPath, valueBytes);
124 | } else {
125 | curator.create().creatingParentsIfNeeded().withMode(CreateMode.CONTAINER).forPath(keyPath, valueBytes);
126 | }
127 | return value;
128 | } catch (Exception e) {
129 | throw new VertxException(e);
130 | }
131 | }
132 |
133 | @Override
134 | public V remove(Object key) {
135 | try {
136 | V result = get(key);
137 | if (result != null) curator.delete().deletingChildrenIfNeeded().forPath(keyPath((K) key));
138 | return result;
139 | } catch (Exception e) {
140 | throw new VertxException(e);
141 | }
142 | }
143 |
144 | @Override
145 | public void putAll(Map extends K, ? extends V> m) {
146 | m.entrySet().stream().forEach(entry -> put(entry.getKey(), entry.getValue()));
147 | }
148 |
149 | @Override
150 | public void clear() {
151 | try {
152 | curator.delete().deletingChildrenIfNeeded().forPath(mapPath);
153 | curator.create().creatingParentsIfNeeded().withMode(CreateMode.CONTAINER).forPath(mapPath);
154 | } catch (Exception e) {
155 | throw new VertxException(e);
156 | }
157 | }
158 |
159 | @Override
160 | public Set keySet() {
161 | try {
162 | syncKeyPath(mapPath);
163 | return curator.getChildren().forPath(mapPath).stream().map(k -> {
164 | try {
165 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath((K) k)));
166 | return keyValue.getKey();
167 | } catch (KeeperException.NoNodeException nodeLostEx) {
168 | logger.warn("node lost " + nodeLostEx.getMessage());
169 | return null;
170 | } catch (Exception ex) {
171 | throw new VertxException(ex);
172 | }
173 | }).collect(Collectors.toSet());
174 | } catch (Exception ex) {
175 | throw new VertxException(ex);
176 | }
177 | }
178 |
179 | @Override
180 | public Collection values() {
181 | try {
182 | syncKeyPath(mapPath);
183 | return curator.getChildren().forPath(mapPath).stream()
184 | .map(k -> {
185 | try {
186 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath((K) k)));
187 | return keyValue.getValue();
188 | } catch (Exception ex) {
189 | throw new VertxException(ex);
190 | }
191 | }
192 | ).collect(Collectors.toSet());
193 | } catch (Exception ex) {
194 | throw new VertxException(ex);
195 | }
196 | }
197 |
198 | private void syncKeyPath(String path) {
199 | //sync always run in background, so we have to using latch to wait callback.
200 | CountDownLatch latch = new CountDownLatch(1);
201 | try {
202 | curator.sync().inBackground((client, event) -> {
203 | if (client.getState() == CuratorFrameworkState.STOPPED) {
204 | latch.countDown();
205 | return;
206 | }
207 | if (event.getPath().equals(path) && event.getType() == CuratorEventType.SYNC) {
208 | latch.countDown();
209 | }
210 | }).forPath(path);
211 | latch.await(3L, TimeUnit.SECONDS);
212 | } catch (Exception e) {
213 | if (e instanceof InterruptedException) {
214 | Thread.currentThread().interrupt();
215 | }
216 | if (!(e instanceof KeeperException.NoNodeException)) {
217 | throw new VertxException(e);
218 | }
219 | }
220 | }
221 |
222 | @Override
223 | public Set> entrySet() {
224 | return keySet().stream().map(k -> {
225 | V v = get(k);
226 | return new HashMap.SimpleImmutableEntry<>(k, v);
227 | }).collect(Collectors.toSet());
228 | }
229 |
230 | static class KeyValue implements Serializable {
231 | private static final long serialVersionUID = 6529685098267757690L;
232 | private K key;
233 | private V value;
234 |
235 | public KeyValue(K key, V value) {
236 | this.key = key;
237 | this.value = value;
238 | }
239 |
240 | public K getKey() {
241 | return key;
242 | }
243 |
244 | public V getValue() {
245 | return value;
246 | }
247 | }
248 |
249 | }
250 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/SubsMapHelper.java:
--------------------------------------------------------------------------------
1 | package io.vertx.spi.cluster.zookeeper.impl;
2 |
3 | import io.vertx.core.Completable;
4 | import io.vertx.core.Future;
5 | import io.vertx.core.Promise;
6 | import io.vertx.core.VertxException;
7 | import io.vertx.core.buffer.Buffer;
8 | import io.vertx.core.internal.VertxInternal;
9 | import io.vertx.core.internal.logging.Logger;
10 | import io.vertx.core.internal.logging.LoggerFactory;
11 | import io.vertx.core.spi.cluster.RegistrationInfo;
12 | import io.vertx.core.spi.cluster.RegistrationListener;
13 | import io.vertx.core.spi.cluster.RegistrationUpdateEvent;
14 | import org.apache.curator.framework.CuratorFramework;
15 | import org.apache.curator.framework.api.CuratorEventType;
16 | import org.apache.curator.framework.recipes.cache.ChildData;
17 | import org.apache.curator.framework.recipes.cache.TreeCache;
18 | import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
19 | import org.apache.curator.framework.recipes.cache.TreeCacheListener;
20 | import org.apache.zookeeper.CreateMode;
21 |
22 | import java.util.*;
23 | import java.util.concurrent.ConcurrentHashMap;
24 | import java.util.concurrent.ConcurrentMap;
25 | import java.util.function.BiFunction;
26 | import java.util.function.Function;
27 |
28 | public class SubsMapHelper implements TreeCacheListener {
29 |
30 | private final CuratorFramework curator;
31 | private final TreeCache treeCache;
32 | private final VertxInternal vertx;
33 | private final RegistrationListener registrationListener;
34 | private final String nodeId;
35 | private final ConcurrentMap> ownSubs = new ConcurrentHashMap<>();
36 | private final ConcurrentMap> localSubs = new ConcurrentHashMap<>();
37 |
38 | private static final String VERTX_SUBS_NAME = "/__vertx.subs";
39 | private static final Logger log = LoggerFactory.getLogger(SubsMapHelper.class);
40 |
41 | private static final Function keyPath = address -> VERTX_SUBS_NAME + "/" + address;
42 | private static final Function valuePath = registrationInfo -> registrationInfo.nodeId() + "-" + registrationInfo.seq();
43 | private static final BiFunction fullPath = (address, registrationInfo) -> keyPath.apply(address) + "/" + valuePath.apply(registrationInfo);
44 |
45 | public SubsMapHelper(CuratorFramework curator, VertxInternal vertx, RegistrationListener registrationListener, String nodeId) {
46 | this.curator = curator;
47 | this.vertx = vertx;
48 | this.treeCache = new TreeCache(curator, VERTX_SUBS_NAME);
49 | this.treeCache.getListenable().addListener(this);
50 | try {
51 | this.treeCache.start();
52 | } catch (Exception e) {
53 | throw new VertxException(e);
54 | }
55 | this.registrationListener = registrationListener;
56 | this.nodeId = nodeId;
57 | }
58 |
59 | public void close() {
60 | treeCache.close();
61 | }
62 |
63 | public void put(String address, RegistrationInfo registrationInfo, Completable promise) {
64 | if (registrationInfo.localOnly()) {
65 | localSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr));
66 | fireRegistrationUpdateEvent(address);
67 | promise.succeed();
68 | } else {
69 | try {
70 | Buffer buffer = Buffer.buffer();
71 | registrationInfo.writeToBuffer(buffer);
72 | curator.create().orSetData().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground((c, e) -> {
73 | if (e.getType() == CuratorEventType.CREATE || e.getType() == CuratorEventType.SET_DATA) {
74 | vertx.runOnContext(Avoid -> {
75 | ownSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr));
76 | promise.succeed();
77 | });
78 | }
79 | }).withUnhandledErrorListener(log::error).forPath(fullPath.apply(address, registrationInfo), buffer.getBytes());
80 | } catch (Exception e) {
81 | log.error(String.format("create subs address %s failed.", address), e);
82 | }
83 | }
84 | }
85 |
86 | private Set addToSet(RegistrationInfo registrationInfo, Set curr) {
87 | Set res = curr != null ? curr : Collections.synchronizedSet(new LinkedHashSet<>());
88 | res.add(registrationInfo);
89 | return res;
90 | }
91 |
92 | public List get(String address) {
93 | Map map = treeCache.getCurrentChildren(keyPath.apply(address));
94 | Collection remote = (map == null) ? Collections.emptyList() : map.values();
95 |
96 | List list;
97 | int size;
98 | size = remote.size();
99 | Set local = localSubs.get(address);
100 | if (local != null) {
101 | synchronized (local) {
102 | size += local.size();
103 | if (size == 0) {
104 | return Collections.emptyList();
105 | }
106 | list = new ArrayList<>(size);
107 | list.addAll(local);
108 | }
109 | } else if (size == 0) {
110 | return Collections.emptyList();
111 | } else {
112 | list = new ArrayList<>(size);
113 | }
114 | for (ChildData childData : remote) {
115 | list.add(toRegistrationInfo(childData));
116 | }
117 | return list;
118 | }
119 |
120 | private static RegistrationInfo toRegistrationInfo(ChildData childData) {
121 | RegistrationInfo registrationInfo = new RegistrationInfo();
122 | Buffer buffer = Buffer.buffer(childData.getData());
123 | registrationInfo.readFromBuffer(0, buffer);
124 | return registrationInfo;
125 | }
126 |
127 | public void remove(String address, RegistrationInfo registrationInfo, Completable promise) {
128 | try {
129 | if (registrationInfo.localOnly()) {
130 | localSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr));
131 | fireRegistrationUpdateEvent(address);
132 | promise.succeed();
133 | } else {
134 | curator.delete().guaranteed().inBackground((c, e) -> {
135 | if (e.getType() == CuratorEventType.DELETE) {
136 | vertx.runOnContext(aVoid -> {
137 | ownSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr));
138 | promise.succeed();
139 | });
140 | }
141 | }).forPath(fullPath.apply(address, registrationInfo));
142 | }
143 | } catch (Exception e) {
144 | log.error(String.format("remove subs address %s failed.", address), e);
145 | promise.fail(e);
146 | }
147 | }
148 |
149 | private Set removeFromSet(RegistrationInfo registrationInfo, Set curr) {
150 | curr.remove(registrationInfo);
151 | return curr.isEmpty() ? null : curr;
152 | }
153 |
154 | @Override
155 | public void childEvent(CuratorFramework client, TreeCacheEvent event) {
156 | switch (event.getType()) {
157 | case NODE_ADDED:
158 | case NODE_UPDATED:
159 | case NODE_REMOVED:
160 | // /__vertx.subs/AddressName/NodeId -> AddressName
161 | String[] pathElements = event.getData().getPath().split("/", 4);
162 | if (pathElements.length <= 3) {
163 | // "/__vertx.subs" and "/__vertx.subs/XX" added event
164 | break;
165 | }
166 | String addr = pathElements[2];
167 | vertx.>executeBlocking(() -> get(addr), false).onComplete(ar -> {
168 | if (ar.succeeded()) {
169 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(addr, ar.result()));
170 | } else {
171 | log.trace("A failure occured while retrieving the updated registrations", ar.cause());
172 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(addr, Collections.emptyList()));
173 | }
174 | });
175 | break;
176 | case CONNECTION_SUSPENDED:
177 | log.warn(String.format("vertx node %s which connected to zookeeper have been suspended.", nodeId));
178 | break;
179 | case CONNECTION_LOST:
180 | log.warn(String.format("vertx node %s which connected to zookeeper has lost", nodeId));
181 | break;
182 | case CONNECTION_RECONNECTED:
183 | log.info(String.format("vertx node %s have reconnected to zookeeper", nodeId));
184 | vertx.runOnContext(aVoid -> {
185 | List> futures = new ArrayList<>();
186 | for (Map.Entry> entry : ownSubs.entrySet()) {
187 | for (RegistrationInfo registrationInfo : entry.getValue()) {
188 | Promise promise = Promise.promise();
189 | put(entry.getKey(), registrationInfo, promise);
190 | futures.add(promise.future());
191 | }
192 | }
193 | Future.all(futures).onComplete(ar -> {
194 | if (ar.failed()) {
195 | log.error("recover node subs information failed.", ar.cause());
196 | } else {
197 | log.info("recover node subs success.");
198 | }
199 | });
200 | });
201 | break;
202 | }
203 | }
204 |
205 | private void fireRegistrationUpdateEvent(String address) {
206 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(address, get(address)));
207 | }
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKAsyncMap.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2016 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.spi.cluster.zookeeper.impl;
17 |
18 | import io.vertx.core.Future;
19 | import io.vertx.core.Promise;
20 | import io.vertx.core.Vertx;
21 | import io.vertx.core.internal.VertxInternal;
22 | import io.vertx.core.shareddata.AsyncMap;
23 | import org.apache.curator.framework.CuratorFramework;
24 | import org.apache.curator.framework.api.CuratorEventType;
25 | import org.apache.zookeeper.data.Stat;
26 |
27 | import java.io.IOException;
28 | import java.time.Instant;
29 | import java.util.*;
30 |
31 | /**
32 | * Created by Stream.Liu
33 | */
34 | public class ZKAsyncMap extends ZKMap implements AsyncMap {
35 |
36 | public ZKAsyncMap(Vertx vertx, CuratorFramework curator, String mapName) {
37 | super(curator, vertx, ZK_PATH_ASYNC_MAP, mapName);
38 | }
39 |
40 | @Override
41 | public Future get(K k) {
42 | return assertKeyIsNotNull(k)
43 | .compose(aVoid -> checkExists(k))
44 | .compose(checkResult -> {
45 | Promise promise = Promise.promise();
46 | if (checkResult) {
47 | try {
48 | curator.getData().inBackground((c,e) -> {
49 | if (e.getType() == CuratorEventType.GET_DATA) {
50 | V value = asObject(e.getData());
51 | vertx.runOnContext(aVoid -> promise.complete(value));
52 | }
53 | }).forPath(keyPath(k));
54 | } catch (Exception e) {
55 | promise.fail(e);
56 | }
57 | } else {
58 | //ignore
59 | promise.complete();
60 | }
61 | return promise.future();
62 | });
63 | }
64 |
65 | @Override
66 | public Future put(K k, V v) {
67 | return put(k, v, Optional.empty());
68 | }
69 |
70 | @Override
71 | public Future put(K k, V v, long ttl) {
72 | return put(k, v, Optional.of(ttl));
73 | }
74 |
75 | private Future put(K k, V v, Optional timeoutOptional) {
76 | return assertKeyAndValueAreNotNull(k, v)
77 | .compose(aVoid -> checkExists(k))
78 | .compose(checkResult -> checkResult ? setData(k, v) : create(k, v, timeoutOptional))
79 | .compose(stat -> Future.succeededFuture());
80 | }
81 |
82 | @Override
83 | public Future putIfAbsent(K k, V v) {
84 | return putIfAbsent(k, v, Optional.empty());
85 | }
86 |
87 | @Override
88 | public Future putIfAbsent(K k, V v, long ttl) {
89 | return putIfAbsent(k, v, Optional.of(ttl));
90 | }
91 |
92 | private Future putIfAbsent(K k, V v, Optional timeoutOptional) {
93 | return assertKeyAndValueAreNotNull(k, v)
94 | .compose(aVoid -> get(k))
95 | .compose(value -> {
96 | if (value == null) {
97 | if (timeoutOptional.isPresent()) {
98 | return put(k, v, timeoutOptional).compose(aVoid -> Future.succeededFuture(null));
99 | } else {
100 | return put(k, v).compose(aVoid -> Future.succeededFuture(null));
101 | }
102 | } else {
103 | return Future.succeededFuture(value);
104 | }
105 | });
106 | }
107 |
108 | @Override
109 | public Future remove(K k) {
110 | return assertKeyIsNotNull(k).compose(aVoid -> {
111 | Promise promise = Promise.promise();
112 | get(k).onComplete(promise);
113 | return promise.future();
114 | }).compose(value -> {
115 | Promise promise = Promise.promise();
116 | if (value != null) {
117 | return delete(k, value);
118 | } else {
119 | promise.complete();
120 | }
121 | return promise.future();
122 | });
123 | }
124 |
125 | @Override
126 | public Future removeIfPresent(K k, V v) {
127 | return assertKeyAndValueAreNotNull(k, v)
128 | .compose(aVoid -> {
129 | Promise promise = Promise.promise();
130 | get(k).onComplete(promise);
131 | return promise.future();
132 | })
133 | .compose(value -> {
134 | Promise promise = Promise.promise();
135 | if (Objects.equals(value, v)) {
136 | delete(k, v).onComplete(deleteResult -> {
137 | if (deleteResult.succeeded()) promise.complete(true);
138 | else promise.fail(deleteResult.cause());
139 | });
140 | } else {
141 | promise.complete(false);
142 | }
143 | return promise.future();
144 | });
145 | }
146 |
147 | @Override
148 | public Future replace(K k, V v) {
149 | return assertKeyAndValueAreNotNull(k, v)
150 | .compose(aVoid -> {
151 | Promise innerPromise = Promise.promise();
152 | vertx.executeBlocking(() -> {
153 | long startTime = Instant.now().toEpochMilli();
154 | int retries = 0;
155 |
156 | for (; ; ) {
157 | Stat stat = new Stat();
158 | String path = keyPath(k);
159 | V currentValue = getData(stat, path);
160 | //do not replace value if previous value is null
161 | if (currentValue == null) {
162 | return null;
163 | }
164 | if (compareAndSet(startTime, retries++, stat, path, currentValue, v)) {
165 | return currentValue;
166 | }
167 | }
168 | }, false).onComplete(innerPromise);
169 | return innerPromise.future();
170 | });
171 | }
172 |
173 | @Override
174 | public Future replaceIfPresent(K k, V oldValue, V newValue) {
175 | return assertKeyIsNotNull(k)
176 | .compose(aVoid -> assertValueIsNotNull(oldValue))
177 | .compose(aVoid -> assertValueIsNotNull(newValue))
178 | .compose(aVoid -> {
179 | Promise innerPromise = Promise.promise();
180 | vertx.executeBlocking(() -> {
181 | long startTime = Instant.now().toEpochMilli();
182 | int retries = 0;
183 |
184 | for (; ; ) {
185 | Stat stat = new Stat();
186 | String path = keyPath(k);
187 | V currentValue = getData(stat, path);
188 | if (!currentValue.equals(oldValue)) {
189 | return false;
190 | }
191 | if (compareAndSet(startTime, retries++, stat, path, oldValue, newValue)) {
192 | return true;
193 | }
194 | }
195 | }, false).onComplete(innerPromise);
196 | return innerPromise.future();
197 | });
198 | }
199 |
200 | @Override
201 | public Future clear() {
202 | //just remove parent node
203 | return delete(mapPath, null).mapEmpty();
204 | }
205 |
206 | @Override
207 | public Future size() {
208 | Promise promise = ((VertxInternal)vertx).getOrCreateContext().promise();
209 | try {
210 | curator.getChildren().inBackground((client, event) ->
211 | promise.tryComplete(event.getChildren().size()))
212 | .forPath(mapPath);
213 | } catch (Exception e) {
214 | promise.tryFail(e);
215 | }
216 | return promise.future();
217 | }
218 |
219 | @Override
220 | public Future> keys() {
221 | Promise> promise = ((VertxInternal)vertx).getOrCreateContext().promise();
222 | try {
223 | curator.getChildren().inBackground((client, event) -> {
224 | Set keys = new HashSet<>();
225 | for (String base64Key : event.getChildren()) {
226 | byte[] binaryKey = Base64.getUrlDecoder().decode(base64Key);
227 | K key;
228 | try {
229 | key = asObject(binaryKey);
230 | } catch (Exception e) {
231 | promise.tryFail(e);
232 | return;
233 | }
234 | keys.add(key);
235 | }
236 | promise.tryComplete(keys);
237 | }).forPath(mapPath);
238 | } catch (Exception e) {
239 | promise.tryFail(e);
240 | }
241 | return promise.future();
242 | }
243 |
244 | @Override
245 | public Future> values() {
246 | Promise> keysPromise = ((VertxInternal)vertx).getOrCreateContext().promise();
247 | keys().onComplete(keysPromise);
248 | return keysPromise.future().compose(keys -> {
249 | List> futures = new ArrayList<>(keys.size());
250 | for (K k : keys) {
251 | Promise valuePromise = Promise.promise();
252 | get(k).onComplete(valuePromise);
253 | futures.add(valuePromise.future());
254 | }
255 | return Future.all(futures).map(compositeFuture -> {
256 | List values = new ArrayList<>(compositeFuture.size());
257 | for (int i = 0; i < compositeFuture.size(); i++) {
258 | values.add(compositeFuture.resultAt(i));
259 | }
260 | return values;
261 | });
262 | });
263 | }
264 |
265 | @Override
266 | public Future