diff --git a/apache-curator/pom.xml b/apache-curator/pom.xml
new file mode 100644
index 0000000000..36c3949b1a
--- /dev/null
+++ b/apache-curator/pom.xml
@@ -0,0 +1,68 @@
+
+ 4.0.0
+ apache-curator
+ 0.0.1-SNAPSHOT
+ jar
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+ 4.0.1
+ 3.4.11
+ 2.9.4
+
+
+ 3.6.1
+
+
+
+
+
+
+
+
+ org.apache.curator
+ curator-x-async
+ ${curator.version}
+
+
+ org.apache.zookeeper
+ zookeeper
+
+
+
+
+
+ org.apache.curator
+ curator-recipes
+ ${curator.version}
+
+
+
+ org.apache.zookeeper
+ zookeeper
+ ${zookeeper.version}
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson-databind.version}
+
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
diff --git a/apache-curator/src/main/java/com/baeldung/apache/curator/modeled/HostConfig.java b/apache-curator/src/main/java/com/baeldung/apache/curator/modeled/HostConfig.java
new file mode 100644
index 0000000000..bab7133742
--- /dev/null
+++ b/apache-curator/src/main/java/com/baeldung/apache/curator/modeled/HostConfig.java
@@ -0,0 +1,31 @@
+package com.baeldung.apache.curator.modeled;
+
+public class HostConfig {
+ private String hostname;
+ private int port;
+
+ public HostConfig() {
+
+ }
+
+ public HostConfig(String hostname, int port) {
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+}
diff --git a/apache-curator/src/test/java/com/baeldung/apache/curator/BaseTest.java b/apache-curator/src/test/java/com/baeldung/apache/curator/BaseTest.java
new file mode 100644
index 0000000000..cfac3ee3f2
--- /dev/null
+++ b/apache-curator/src/test/java/com/baeldung/apache/curator/BaseTest.java
@@ -0,0 +1,22 @@
+package com.baeldung.apache.curator;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryNTimes;
+import org.junit.Before;
+
+public abstract class BaseTest {
+
+ @Before
+ public void setup() {
+ org.apache.log4j.BasicConfigurator.configure();
+ }
+
+ protected CuratorFramework newClient() {
+ int sleepMsBetweenRetries = 100;
+ int maxRetries = 3;
+ RetryPolicy retryPolicy = new RetryNTimes(maxRetries, sleepMsBetweenRetries);
+ return CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy);
+ }
+}
diff --git a/apache-curator/src/test/java/com/baeldung/apache/curator/configuration/ConfigurationManagementManualTest.java b/apache-curator/src/test/java/com/baeldung/apache/curator/configuration/ConfigurationManagementManualTest.java
new file mode 100644
index 0000000000..0475f9c237
--- /dev/null
+++ b/apache-curator/src/test/java/com/baeldung/apache/curator/configuration/ConfigurationManagementManualTest.java
@@ -0,0 +1,98 @@
+package com.baeldung.apache.curator.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.junit.Test;
+
+import com.baeldung.apache.curator.BaseTest;
+
+public class ConfigurationManagementManualTest extends BaseTest {
+
+ private static final String KEY_FORMAT = "/%s";
+
+ @Test
+ public void givenPath_whenCreateKey_thenValueIsStored() throws Exception {
+ try (CuratorFramework client = newClient()) {
+ client.start();
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+ String key = getKey();
+ String expected = "my_value";
+
+ // Create key nodes structure
+ client
+ .create()
+ .forPath(key);
+
+ // Set data value for our key
+ async
+ .setData()
+ .forPath(key, expected.getBytes());
+
+ // Get data value
+ AtomicBoolean isEquals = new AtomicBoolean();
+ async
+ .getData()
+ .forPath(key)
+ .thenAccept(data -> isEquals.set(new String(data).equals(expected)));
+
+ Thread.sleep(1000);
+
+ assertThat(isEquals.get()).isTrue();
+ }
+ }
+
+ @Test
+ public void givenPath_whenWatchAKeyAndStoreAValue_thenWatcherIsTriggered() throws Exception {
+ try (CuratorFramework client = newClient()) {
+ client.start();
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+ String key = getKey();
+ String expected = "my_value";
+
+ // Create key structure
+ async
+ .create()
+ .forPath(key);
+
+ List changes = new ArrayList<>();
+
+ // Watch data value
+ async
+ .watched()
+ .getData()
+ .forPath(key)
+ .event()
+ .thenAccept(watchedEvent -> {
+ try {
+ changes.add(new String(client
+ .getData()
+ .forPath(watchedEvent.getPath())));
+ } catch (Exception e) {
+ // fail ...
+ }
+ });
+
+ // Set data value for our key
+ async
+ .setData()
+ .forPath(key, expected.getBytes());
+
+ Thread.sleep(1000);
+
+ assertThat(changes.size() > 0).isTrue();
+ }
+ }
+
+ private String getKey() {
+ return String.format(KEY_FORMAT, UUID
+ .randomUUID()
+ .toString());
+ }
+}
diff --git a/apache-curator/src/test/java/com/baeldung/apache/curator/connection/ConnectionManagementManualTest.java b/apache-curator/src/test/java/com/baeldung/apache/curator/connection/ConnectionManagementManualTest.java
new file mode 100644
index 0000000000..931a977900
--- /dev/null
+++ b/apache-curator/src/test/java/com/baeldung/apache/curator/connection/ConnectionManagementManualTest.java
@@ -0,0 +1,69 @@
+package com.baeldung.apache.curator.connection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryNTimes;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.junit.Test;
+
+public class ConnectionManagementManualTest {
+
+ @Test
+ public void givenRunningZookeeper_whenOpenConnection_thenClientIsOpened() throws Exception {
+ int sleepMsBetweenRetries = 100;
+ int maxRetries = 3;
+ RetryPolicy retryPolicy = new RetryNTimes(maxRetries, sleepMsBetweenRetries);
+
+ try (CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy)) {
+ client.start();
+ assertThat(client
+ .checkExists()
+ .forPath("/")).isNotNull();
+ }
+ }
+
+ @Test
+ public void givenRunningZookeeper_whenOpenConnectionUsingAsyncNotBlocking_thenClientIsOpened() throws InterruptedException {
+ int sleepMsBetweenRetries = 100;
+ int maxRetries = 3;
+ RetryPolicy retryPolicy = new RetryNTimes(maxRetries, sleepMsBetweenRetries);
+
+ try (CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy)) {
+ client.start();
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+
+ AtomicBoolean exists = new AtomicBoolean(false);
+ async
+ .checkExists()
+ .forPath("/")
+ .thenAcceptAsync(s -> exists.set(s != null));
+ Thread.sleep(100);
+ assertThat(exists.get()).isTrue();
+ }
+ }
+
+ @Test
+ public void givenRunningZookeeper_whenOpenConnectionUsingAsyncBlocking_thenClientIsOpened() throws InterruptedException {
+ int sleepMsBetweenRetries = 100;
+ int maxRetries = 3;
+ RetryPolicy retryPolicy = new RetryNTimes(maxRetries, sleepMsBetweenRetries);
+
+ try (CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy)) {
+ client.start();
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+
+ AtomicBoolean exists = new AtomicBoolean(false);
+ async
+ .checkExists()
+ .forPath("/")
+ .thenAccept(s -> exists.set(s != null));
+ Thread.sleep(100);
+ assertThat(exists.get()).isTrue();
+ }
+ }
+}
diff --git a/apache-curator/src/test/java/com/baeldung/apache/curator/modeled/ModelTypedExamplesManualTest.java b/apache-curator/src/test/java/com/baeldung/apache/curator/modeled/ModelTypedExamplesManualTest.java
new file mode 100644
index 0000000000..9d00c0a4c2
--- /dev/null
+++ b/apache-curator/src/test/java/com/baeldung/apache/curator/modeled/ModelTypedExamplesManualTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.apache.curator.modeled;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.junit.Test;
+
+import com.baeldung.apache.curator.BaseTest;
+
+public class ModelTypedExamplesManualTest extends BaseTest {
+
+ @Test
+ public void givenPath_whenStoreAModel_thenNodesAreCreated() throws InterruptedException {
+
+ ModelSpec mySpec = ModelSpec
+ .builder(ZPath.parseWithIds("/config/dev"), JacksonModelSerializer.build(HostConfig.class))
+ .build();
+
+ try (CuratorFramework client = newClient()) {
+ client.start();
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+ ModeledFramework modeledClient = ModeledFramework.wrap(async, mySpec);
+
+ modeledClient.set(new HostConfig("host-name", 8080));
+
+ modeledClient
+ .read()
+ .whenComplete((value, e) -> {
+ if (e != null) {
+ fail("Cannot read host config", e);
+ } else {
+ assertThat(value).isNotNull();
+ assertThat(value.getHostname()).isEqualTo("host-name");
+ assertThat(value.getPort()).isEqualTo(8080);
+ }
+
+ });
+ }
+
+ }
+}
diff --git a/apache-curator/src/test/java/com/baeldung/apache/curator/recipes/RecipesManualTest.java b/apache-curator/src/test/java/com/baeldung/apache/curator/recipes/RecipesManualTest.java
new file mode 100644
index 0000000000..04f4104e6b
--- /dev/null
+++ b/apache-curator/src/test/java/com/baeldung/apache/curator/recipes/RecipesManualTest.java
@@ -0,0 +1,74 @@
+package com.baeldung.apache.curator.recipes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.leader.LeaderSelector;
+import org.apache.curator.framework.recipes.leader.LeaderSelectorListener;
+import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
+import org.apache.curator.framework.recipes.shared.SharedCount;
+import org.apache.curator.framework.state.ConnectionState;
+import org.junit.Test;
+
+import com.baeldung.apache.curator.BaseTest;
+
+public class RecipesManualTest extends BaseTest {
+
+ @Test
+ public void givenRunningZookeeper_whenUsingLeaderElection_thenNoErrors() {
+ try (CuratorFramework client = newClient()) {
+ client.start();
+ LeaderSelector leaderSelector = new LeaderSelector(client, "/mutex/select/leader/for/job/A", new LeaderSelectorListener() {
+
+ @Override
+ public void stateChanged(CuratorFramework client, ConnectionState newState) {
+
+ }
+
+ @Override
+ public void takeLeadership(CuratorFramework client) throws Exception {
+ // I'm the leader of the job A !
+ }
+
+ });
+
+ leaderSelector.start();
+
+ // Wait until the job A is done among all the members
+
+ leaderSelector.close();
+ }
+ }
+
+ @Test
+ public void givenRunningZookeeper_whenUsingSharedLock_thenNoErrors() throws Exception {
+ try (CuratorFramework client = newClient()) {
+ client.start();
+ InterProcessSemaphoreMutex sharedLock = new InterProcessSemaphoreMutex(client, "/mutex/process/A");
+
+ sharedLock.acquire();
+
+ // Do process A
+
+ sharedLock.release();
+ }
+ }
+
+ @Test
+ public void givenRunningZookeeper_whenUsingSharedCounter_thenCounterIsIncrement() throws Exception {
+ try (CuratorFramework client = newClient()) {
+ client.start();
+
+ try (SharedCount counter = new SharedCount(client, "/counters/A", 0)) {
+ counter.start();
+
+ counter.setCount(0);
+ counter.setCount(counter.getCount() + 1);
+
+ assertThat(counter.getCount()).isEqualTo(1);
+ }
+
+ }
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 75cf4b84b8..23f14a56a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
apache-fop
apache-poi
apache-thrift
+ apache-curator
autovalue
axon
bootique