You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@curator.apache.org by ra...@apache.org on 2017/07/08 03:32:37 UTC
[5/6] curator git commit: Squashed commit of the following:
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Priority.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Priority.java b/curator-examples/src/main/java/pubsub/models/Priority.java
new file mode 100644
index 0000000..3b10f75
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Priority.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.models;
+
+public enum Priority
+{
+ low,
+ medium,
+ high
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/resources/log4j.properties b/curator-examples/src/main/resources/log4j.properties
new file mode 100644
index 0000000..0405670
--- /dev/null
+++ b/curator-examples/src/main/resources/log4j.properties
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+log4j.rootLogger=ERROR, console
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/site/confluence/index.confluence
----------------------------------------------------------------------
diff --git a/curator-examples/src/site/confluence/index.confluence b/curator-examples/src/site/confluence/index.confluence
index 928b44f..f9be506 100644
--- a/curator-examples/src/site/confluence/index.confluence
+++ b/curator-examples/src/site/confluence/index.confluence
@@ -7,6 +7,7 @@ This module contains example usages of various Curator features. Each directory
|/locking|Example of using InterProcessMutex|
|/discovery|Example usage of the Curator's ServiceDiscovery|
|/framework|A few examples of how to use the CuratorFramework class|
+|/async|Example AsyncCuratorFramework code|
+|/modeled|ModeledFramework and Modeled Cache examples|
-See the [examples source repo|https://git-wip-us.apache.org/repos/asf?p=curator.git;a=tree;f=curator-examples/src/main/java] for each example.
-
+See the [examples source repo|https://github.com/apache/curator/tree/master/curator-examples/src/main/java] for each example.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
index 59b3510..b310f06 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
@@ -32,5 +32,15 @@ public interface CreateBuilder extends CreateBuilderMain
*/
CreateBuilderMain withTtl(long ttl);
+ /**
+ * If the ZNode already exists, Curator will instead call setData()
+ */
CreateBuilder2 orSetData();
+
+ /**
+ * If the ZNode already exists, Curator will instead call setData()
+ *
+ * @param version the version to use for {@link org.apache.curator.framework.CuratorFramework#setData()}
+ */
+ CreateBuilder2 orSetData(int version);
}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
index fdd1e15..406d972 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
@@ -52,6 +52,7 @@ public class CreateBuilderImpl implements CreateBuilder, CreateBuilder2, Backgro
private boolean doProtected;
private boolean compress;
private boolean setDataIfExists;
+ private int setDataIfExistsVersion = -1;
private String protectedId;
private ACLing acling;
private Stat storingStat;
@@ -95,10 +96,22 @@ public class CreateBuilderImpl implements CreateBuilder, CreateBuilder2, Backgro
this.ttl = ttl;
}
+ public void setSetDataIfExistsVersion(int version)
+ {
+ this.setDataIfExistsVersion = version;
+ }
+
@Override
public CreateBuilder2 orSetData()
{
+ return orSetData(-1);
+ }
+
+ @Override
+ public CreateBuilder2 orSetData(int version)
+ {
setDataIfExists = true;
+ setDataIfExistsVersion = version;
return this;
}
@@ -751,7 +764,7 @@ public class CreateBuilderImpl implements CreateBuilder, CreateBuilder2, Backgro
{
try
{
- client.getZooKeeper().setData(path, mainOperationAndData.getData().getData(), -1, statCallback, backgrounding.getContext());
+ client.getZooKeeper().setData(path, mainOperationAndData.getData().getData(), setDataIfExistsVersion, statCallback, backgrounding.getContext());
}
catch ( KeeperException e )
{
@@ -1078,7 +1091,7 @@ public class CreateBuilderImpl implements CreateBuilder, CreateBuilder2, Backgro
{
if ( setDataIfExists )
{
- client.getZooKeeper().setData(path, data, -1);
+ client.getZooKeeper().setData(path, data, setDataIfExistsVersion);
createdPath = path;
}
else
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-framework/src/main/java/org/apache/curator/framework/schema/Schema.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/schema/Schema.java b/curator-framework/src/main/java/org/apache/curator/framework/schema/Schema.java
index e9f4f18..bcb35d3 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/schema/Schema.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/schema/Schema.java
@@ -318,7 +318,7 @@ public class Schema
", pathRegex=" + pathRegex +
", path='" + fixedPath + '\'' +
", documentation='" + documentation + '\'' +
- ", dataValidator=" + schemaValidator +
+ ", dataValidator=" + schemaValidator.getClass() +
", ephemeral=" + ephemeral +
", sequential=" + sequential +
", watched=" + watched +
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
----------------------------------------------------------------------
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
index 9a6eaa7..9687e1b 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
@@ -145,6 +145,11 @@ public class NodeCache implements Closeable
this.dataIsCompressed = dataIsCompressed;
}
+ public CuratorFramework getClient()
+ {
+ return client;
+ }
+
/**
* Start the cache. The cache is not started automatically. You must call this method.
*
@@ -233,6 +238,16 @@ public class NodeCache implements Closeable
return data.get();
}
+ /**
+ * Return the path this cache is watching
+ *
+ * @return path
+ */
+ public String getPath()
+ {
+ return path;
+ }
+
@VisibleForTesting
volatile Exchanger<Object> rebuildTestExchanger;
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
----------------------------------------------------------------------
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
index d11ced6..c5449f2 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
@@ -133,7 +133,7 @@ public class PathChildrenCache implements Closeable
handleStateChange(newState);
}
};
- private static final ThreadFactory defaultThreadFactory = ThreadUtils.newThreadFactory("PathChildrenCache");
+ public static final ThreadFactory defaultThreadFactory = ThreadUtils.newThreadFactory("PathChildrenCache");
/**
* @param client the client
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
----------------------------------------------------------------------
diff --git a/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java b/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
index 5114552..8238c82 100644
--- a/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
+++ b/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
@@ -27,12 +27,12 @@ import org.testng.IRetryAnalyzer;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
-import org.testng.TestListenerAdapter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import java.io.IOException;
import java.net.BindException;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -155,7 +155,7 @@ public class BaseClassForTests
RetryAnalyzer(Logger log, RetryContext retryContext)
{
this.log = log;
- this.retryContext = retryContext;
+ this.retryContext = Objects.requireNonNull(retryContext, "retryContext cannot be null");
}
@Override
@@ -218,7 +218,11 @@ public class BaseClassForTests
}
else if ( method.isTestMethod() )
{
- method.getTestMethod().setRetryAnalyzer(new RetryAnalyzer(log, (RetryContext)context.getAttribute(ATTRIBUTE_NAME)));
+ RetryContext retryContext = (RetryContext)context.getAttribute(ATTRIBUTE_NAME);
+ if ( retryContext != null )
+ {
+ method.getTestMethod().setRetryAnalyzer(new RetryAnalyzer(log, retryContext));
+ }
}
}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/pom.xml
----------------------------------------------------------------------
diff --git a/curator-x-async/pom.xml b/curator-x-async/pom.xml
index a7fdcd5..925896b 100644
--- a/curator-x-async/pom.xml
+++ b/curator-x-async/pom.xml
@@ -21,6 +21,18 @@
<dependency>
<groupId>org.apache.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<scope>test</scope>
</dependency>
@@ -30,6 +42,12 @@
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
index 30ed234..ad7547b 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
@@ -27,9 +27,18 @@ import java.util.concurrent.CompletionStage;
public interface AsyncStage<T> extends CompletionStage<T>
{
/**
- * If the {@link org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is
- * used (via {@link AsyncCuratorFramework#watched()}), this returns the completion
- * stage used when the watcher is triggered
+ * <p>
+ * If the {@link org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is
+ * used (via {@link AsyncCuratorFramework#watched()}), this returns the completion
+ * stage used when the watcher is triggered
+ * </p>
+ *
+ * <p>
+ * Also, applies to {@link org.apache.curator.x.async.modeled.ModeledFramework}
+ * when {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#watched(WatchMode)}
+ * or {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#watched(WatchMode, java.util.function.UnaryOperator)}
+ * is used.
+ * </p>
*
* @return CompletionStage for the set watcher or <code>null</code>
*/
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncWrappers.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncWrappers.java b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncWrappers.java
new file mode 100644
index 0000000..e982cf2
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncWrappers.java
@@ -0,0 +1,297 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async;
+
+import org.apache.curator.framework.recipes.locks.InterProcessLock;
+import org.apache.curator.utils.ThreadUtils;
+import org.apache.curator.x.async.modeled.ZPath;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * <p>
+ * Utility for adding asynchronous behavior
+ * </p>
+ *
+ * <p>
+ * E.g. locks:
+ * <code><pre>
+ * InterProcessMutex mutex = new InterProcessMutex(...) // or any InterProcessLock
+ * AsyncWrappers.lockAsync(mutex, executor).thenAccept(dummy -> {
+ * try
+ * {
+ * // do work while holding the lock
+ * }
+ * finally
+ * {
+ * AsyncWrappers.release(mutex);
+ * }
+ * }).exceptionally(e -> {
+ * if ( e instanceOf TimeoutException ) {
+ * // timed out trying to acquire the lock
+ * }
+ * // handle the error
+ * return null;
+ * });
+ * </pre></code>
+ * </p>
+ *
+ * <p>
+ * E.g. EnsureContainers
+ * <code><pre>
+ * AsyncWrappers.(client, path, executor).thenAccept(dummy -> {
+ * // execute after ensuring containers
+ * });
+ * </pre></code>
+ * </p>
+ */
+public class AsyncWrappers
+{
+ /**
+ * Asynchronously call {@link org.apache.curator.framework.CuratorFramework#createContainers(String)} using
+ * the {@link java.util.concurrent.ForkJoinPool#commonPool()}.
+ *
+ * @param client client
+ * @param path path to ensure
+ * @return stage
+ */
+ public static CompletionStage<Void> asyncEnsureContainers(AsyncCuratorFramework client, ZPath path)
+ {
+ return asyncEnsureContainers(client, path, null);
+ }
+
+ /**
+ * Asynchronously call {@link org.apache.curator.framework.CuratorFramework#createContainers(String)} using
+ * the given executor
+ *
+ * @param client client
+ * @param path path to ensure
+ * @return stage
+ */
+ public static CompletionStage<Void> asyncEnsureContainers(AsyncCuratorFramework client, ZPath path, Executor executor)
+ {
+ Runnable proc = () -> {
+ try
+ {
+ client.unwrap().createContainers(path.fullPath());
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException(e);
+ }
+ };
+ return (executor != null) ? CompletableFuture.runAsync(proc, executor) : CompletableFuture.runAsync(proc);
+ }
+
+ /**
+ * Set as the completion stage's exception when trying to acquire a lock
+ * times out
+ */
+ public static class TimeoutException extends RuntimeException
+ {
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously using the given timeout and executor. If the lock
+ * is not acquired within the timeout stage is completedExceptionally with {@link AsyncWrappers.TimeoutException}
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @param timeout max timeout to acquire lock
+ * @param unit time unit of timeout
+ * @param executor executor to use to asynchronously acquire
+ * @return stage
+ */
+ public static CompletionStage<Void> lockAsync(InterProcessLock lock, long timeout, TimeUnit unit, Executor executor)
+ {
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ if ( executor == null )
+ {
+ CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit));
+ }
+ else
+ {
+ CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit), executor);
+ }
+ return future;
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously using the given timeout and executor. The stage
+ * is completed with a Boolean that indicates whether or not the lock was acquired.
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @param timeout max timeout to acquire lock
+ * @param unit time unit of timeout
+ * @param executor executor to use to asynchronously acquire
+ * @return stage
+ */
+ public static CompletionStage<Boolean> lockAsyncIf(InterProcessLock lock, long timeout, TimeUnit unit, Executor executor)
+ {
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ if ( executor == null )
+ {
+ CompletableFuture.runAsync(() -> lockIf(future, lock, timeout, unit));
+ }
+ else
+ {
+ CompletableFuture.runAsync(() -> lockIf(future, lock, timeout, unit), executor);
+ }
+ return future;
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously using the given executor and without a timeout.
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @param executor executor to use to asynchronously acquire
+ * @return stage
+ */
+ public static CompletionStage<Void> lockAsync(InterProcessLock lock, Executor executor)
+ {
+ return lockAsync(lock, 0, null, executor);
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously using the given timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}.
+ * If the lock is not acquired within the timeout stage is completedExceptionally with {@link AsyncWrappers.TimeoutException}
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @param timeout max timeout to acquire lock
+ * @param unit time unit of timeout
+ * @return stage
+ */
+ public static CompletionStage<Void> lockAsync(InterProcessLock lock, long timeout, TimeUnit unit)
+ {
+ return lockAsync(lock, timeout, unit, null);
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously using the given timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}.
+ * The stage is completed with a Boolean that indicates whether or not the lock was acquired.
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @param timeout max timeout to acquire lock
+ * @param unit time unit of timeout
+ * @return stage
+ */
+ public static CompletionStage<Boolean> lockAsyncIf(InterProcessLock lock, long timeout, TimeUnit unit)
+ {
+ return lockAsyncIf(lock, timeout, unit, null);
+ }
+
+ /**
+ * Attempt to acquire the given lock asynchronously without timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}.
+ *
+ * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex},
+ * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.)
+ * @return stage
+ */
+ public static CompletionStage<Void> lockAsync(InterProcessLock lock)
+ {
+ return lockAsync(lock, 0, null, null);
+ }
+
+ /**
+ * Release the lock and wrap any exception in <code>RuntimeException</code>
+ *
+ * @param lock lock to release
+ */
+ public static void release(InterProcessLock lock)
+ {
+ release(lock, true);
+ }
+
+ /**
+ * Release the lock and wrap any exception in <code>RuntimeException</code>
+ *
+ * @param lock lock to release
+ * @param ignoreNoLockExceptions if true {@link java.lang.IllegalStateException} is ignored
+ */
+ public static void release(InterProcessLock lock, boolean ignoreNoLockExceptions)
+ {
+ try
+ {
+ lock.release();
+ }
+ catch ( IllegalStateException e )
+ {
+ if ( !ignoreNoLockExceptions )
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ catch ( Exception e )
+ {
+ ThreadUtils.checkInterrupted(e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void lockIf(CompletableFuture<Boolean> future, InterProcessLock lock, long timeout, TimeUnit unit)
+ {
+ try
+ {
+ future.complete(lock.acquire(timeout, unit));
+ }
+ catch ( Exception e )
+ {
+ ThreadUtils.checkInterrupted(e);
+ future.completeExceptionally(e);
+ }
+ }
+
+ private static void lock(CompletableFuture<Void> future, InterProcessLock lock, long timeout, TimeUnit unit)
+ {
+ try
+ {
+ if ( unit != null )
+ {
+ if ( lock.acquire(timeout, unit) )
+ {
+ future.complete(null);
+ }
+ else
+ {
+ future.completeExceptionally(new TimeoutException());
+ }
+ }
+ else
+ {
+ lock.acquire();
+ future.complete(null);
+ }
+ }
+ catch ( Exception e )
+ {
+ ThreadUtils.checkInterrupted(e);
+ future.completeExceptionally(e);
+ }
+ }
+
+ private AsyncWrappers()
+ {
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCreateBuilder.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCreateBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCreateBuilder.java
index e5f2d8c..7ed934e 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCreateBuilder.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCreateBuilder.java
@@ -67,6 +67,15 @@ public interface AsyncCreateBuilder extends AsyncPathAndBytesable<AsyncStage<Str
AsyncPathAndBytesable<AsyncStage<String>> withTtl(long ttl);
/**
+ * Specify the setData expected matching version when using option
+ * {@link org.apache.curator.x.async.api.CreateOption#setDataIfExists}. By default -1 is used.
+ *
+ * @param version setData expected matching version
+ * @return this for chaining
+ */
+ AsyncPathAndBytesable<AsyncStage<String>> withSetDataVersion(int version);
+
+ /**
* Options to change how the ZNode is created
*
* @param options options
@@ -141,4 +150,23 @@ public interface AsyncCreateBuilder extends AsyncPathAndBytesable<AsyncStage<Str
* @return this
*/
AsyncPathAndBytesable<AsyncStage<String>> withOptions(Set<CreateOption> options, CreateMode createMode, List<ACL> aclList, Stat stat, long ttl);
+
+ /**
+ * set options, mode, ACLs, and stat
+ *
+ * @param options options
+ * @param createMode mode to use
+ * @param aclList the ACL list to use
+ * @param stat the stat to have filled in
+ * @param ttl the ttl or 0
+ * @param setDataVersion the setData matching version or -1
+ * @see #withOptions(java.util.Set)
+ * @see #withMode(org.apache.zookeeper.CreateMode)
+ * @see #withACL(java.util.List)
+ * @see #storingStatIn(org.apache.zookeeper.data.Stat)
+ * @see #withTtl(long)
+ * @see #withSetDataVersion(long)
+ * @return this
+ */
+ AsyncPathAndBytesable<AsyncStage<String>> withOptions(Set<CreateOption> options, CreateMode createMode, List<ACL> aclList, Stat stat, long ttl, int setDataVersion);
}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCuratorFrameworkDsl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCuratorFrameworkDsl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCuratorFrameworkDsl.java
index 0807160..bc66bb6 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCuratorFrameworkDsl.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/api/AsyncCuratorFrameworkDsl.java
@@ -19,7 +19,6 @@
package org.apache.curator.x.async.api;
import org.apache.curator.framework.api.transaction.CuratorOp;
-import org.apache.curator.x.async.WatchMode;
/**
* Zookeeper framework-style client
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCreateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCreateBuilderImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCreateBuilderImpl.java
index e8b1d30..c27639e 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCreateBuilderImpl.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCreateBuilderImpl.java
@@ -44,6 +44,7 @@ class AsyncCreateBuilderImpl implements AsyncCreateBuilder
private Set<CreateOption> options = Collections.emptySet();
private Stat stat = null;
private long ttl = -1;
+ private int setDataVersion = -1;
AsyncCreateBuilderImpl(CuratorFrameworkImpl client, Filters filters)
{
@@ -80,6 +81,13 @@ class AsyncCreateBuilderImpl implements AsyncCreateBuilder
}
@Override
+ public AsyncPathAndBytesable<AsyncStage<String>> withSetDataVersion(int version)
+ {
+ this.setDataVersion = version;
+ return this;
+ }
+
+ @Override
public AsyncPathAndBytesable<AsyncStage<String>> withOptions(Set<CreateOption> options)
{
this.options = Objects.requireNonNull(options, "options cannot be null");
@@ -133,6 +141,18 @@ class AsyncCreateBuilderImpl implements AsyncCreateBuilder
}
@Override
+ public AsyncPathAndBytesable<AsyncStage<String>> withOptions(Set<CreateOption> options, CreateMode createMode, List<ACL> aclList, Stat stat, long ttl, int setDataVersion)
+ {
+ this.options = Objects.requireNonNull(options, "options cannot be null");
+ this.aclList = aclList;
+ this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null");
+ this.stat = stat;
+ this.ttl = ttl;
+ this.setDataVersion = setDataVersion;
+ return this;
+ }
+
+ @Override
public AsyncStage<String> forPath(String path)
{
return internalForPath(path, null, false);
@@ -159,6 +179,7 @@ class AsyncCreateBuilderImpl implements AsyncCreateBuilder
stat,
ttl
);
+ builder.setSetDataIfExistsVersion(setDataVersion);
return safeCall(common.internalCallback, () -> useData ? builder.forPath(path, data) : builder.forPath(path));
}
}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
new file mode 100644
index 0000000..b4e5601
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
@@ -0,0 +1,124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Model serializer that uses Jackson for JSON serialization. <strong>IMPORTANT: </strong>
+ * the jackson dependency is specified as <code>provided</code> in the curator-x-async Maven POM
+ * file to avoid adding a new dependency to Curator. Therefore, if you wish to use the
+ * JacksonModelSerializer you must manually add the dependency to your build system
+ */
+public class JacksonModelSerializer<T> implements ModelSerializer<T>
+{
+ private static final ObjectMapper mapper = new ObjectMapper();
+ static
+ {
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ }
+
+ private final ObjectReader reader;
+ private final ObjectWriter writer;
+
+ public static <T> JacksonModelSerializer<T> build(Class<T> modelClass)
+ {
+ return new JacksonModelSerializer<>(modelClass);
+ }
+
+ public static <T> JacksonModelSerializer<T> build(JavaType type)
+ {
+ return new JacksonModelSerializer<>(type);
+ }
+
+ public static <T> JacksonModelSerializer<T> build(TypeReference type)
+ {
+ return new JacksonModelSerializer<>(type);
+ }
+
+ public JacksonModelSerializer(Class<T> modelClass)
+ {
+ this(mapper.getTypeFactory().constructType(modelClass));
+ }
+
+ public JacksonModelSerializer(JavaType type)
+ {
+ reader = mapper.readerFor(type);
+ writer = mapper.writerFor(type);
+ }
+
+ public JacksonModelSerializer(TypeReference type)
+ {
+ reader = mapper.readerFor(type);
+ writer = mapper.writerFor(type);
+ }
+
+ public JacksonModelSerializer(ObjectMapper mapper, JavaType type)
+ {
+ reader = mapper.readerFor(type);
+ writer = mapper.writerFor(type);
+ }
+
+ public JacksonModelSerializer(ObjectMapper mapper, TypeReference type)
+ {
+ reader = mapper.readerFor(type);
+ writer = mapper.writerFor(type);
+ }
+
+ public JacksonModelSerializer(ObjectReader reader, ObjectWriter writer)
+ {
+ this.reader = Objects.requireNonNull(reader, "reader cannot be null");
+ this.writer = Objects.requireNonNull(writer, "writer cannot be null");
+ }
+
+ @Override
+ public byte[] serialize(T model)
+ {
+ try
+ {
+ return writer.writeValueAsBytes(model);
+ }
+ catch ( JsonProcessingException e )
+ {
+ throw new RuntimeException(String.format("Could not serialize value: %s", model), e);
+ }
+ }
+
+ @Override
+ public T deserialize(byte[] bytes)
+ {
+ try
+ {
+ return reader.readValue(bytes);
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException(String.format("Could not deserialize value: %s", Arrays.toString(bytes)), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
new file mode 100644
index 0000000..428096e
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+/**
+ * Serializing interface for models
+ */
+public interface ModelSerializer<T>
+{
+ /**
+ * Given a model return the serialized bytes
+ *
+ * @param model model
+ * @return bytes
+ */
+ byte[] serialize(T model);
+
+ /**
+ * Given bytes serialized via {@link #serialize(Object)} return
+ * the model
+ *
+ * @param bytes serialized bytes
+ * @return model
+ * @throws RuntimeException if <code>bytes</code> is invalid or there was an error deserializing
+ */
+ T deserialize(byte[] bytes);
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java
new file mode 100644
index 0000000..2fe5242
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.curator.framework.schema.Schema;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.ACL;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A full specification for dealing with a portion of the ZooKeeper tree. ModelSpec's contain:
+ *
+ * <ul>
+ * <li>A node path</li>
+ * <li>Serializer for the data stored</li>
+ * <li>Options for how to create the node (mode, compression, etc.)</li>
+ * <li>Options for how to deleting the node (quietly, guaranteed, etc.)</li>
+ * <li>ACLs</li>
+ * <li>Optional schema generation</li>
+ * </ul>
+ */
+public interface ModelSpec<T> extends Resolvable
+{
+ Set<CreateOption> defaultCreateOptions = ImmutableSet.of(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists);
+ Set<DeleteOption> defaultDeleteOptions = ImmutableSet.of(DeleteOption.guaranteed);
+
+ /**
+ * Start a new ModelSpecBuilder for the given path and serializer. The returned ModelSpecBuilder
+ * uses {@link #defaultCreateOptions} and {@link #defaultDeleteOptions}, but you can change these
+ * with builder methods.
+ *
+ * @param path path to model
+ * @param serializer the model's serializer
+ * @return builder
+ */
+ static <T> ModelSpecBuilder<T> builder(ZPath path, ModelSerializer<T> serializer)
+ {
+ return new ModelSpecBuilder<>(path, serializer)
+ .withCreateOptions(defaultCreateOptions)
+ .withDeleteOptions(defaultDeleteOptions);
+ }
+
+ /**
+ * Start a new ModelSpecBuilder for the given serializer. The returned ModelSpecBuilder
+ * uses {@link #defaultCreateOptions} and {@link #defaultDeleteOptions}, but you can change these
+ * with builder methods. You must set a path before calling {@link ModelSpecBuilder#build()}
+ *
+ * @param serializer the model's serializer
+ * @return builder
+ */
+ static <T> ModelSpecBuilder<T> builder(ModelSerializer<T> serializer)
+ {
+ return new ModelSpecBuilder<>(serializer)
+ .withCreateOptions(defaultCreateOptions)
+ .withDeleteOptions(defaultDeleteOptions);
+ }
+
+ /**
+ * <p>
+ * Return a new CuratorModel instance with all the same options but applying to the given child node of this CuratorModel's
+ * path. E.g. if this CuratorModel instance applies to "/a/b", calling <code>modeled.at("c")</code> returns an instance that applies to
+ * "/a/b/c".
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of child or,
+ * if it implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param child child node.
+ * @return new Modeled Spec instance
+ */
+ ModelSpec<T> child(Object child);
+
+ /**
+ * <p>
+ * Return a new CuratorModel instance with all the same options but applying to the parent node of this CuratorModel's
+ * path. E.g. if this CuratorModel instance applies to "/a/b/c", calling <code>modeled.parent()</code> returns an instance that applies to
+ * "/a/b".
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of child or,
+ * if it implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @return new Modeled Spec instance
+ */
+ ModelSpec<T> parent();
+
+ /**
+ * Return a new CuratorModel instance with all the same options but using the given path.
+ *
+ * @param path new path
+ * @return new Modeled Spec instance
+ */
+ ModelSpec<T> withPath(ZPath path);
+
+ /**
+ * <p>
+ * Return a new CuratorModel instance with all the same options but using a resolved
+ * path by calling {@link org.apache.curator.x.async.modeled.ZPath#resolved(Object...)}
+ * using the given parameters
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of the parameter object or,
+ * if the object implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ModelSpec
+ */
+ @Override
+ ModelSpec<T> resolved(Object... parameters);
+
+ /**
+ * <p>
+ * Return a new CuratorModel instance with all the same options but using a resolved
+ * path by calling {@link org.apache.curator.x.async.modeled.ZPath#resolved(java.util.List)}
+ * using the given parameters
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of the parameter object or,
+ * if the object implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ModelSpec
+ */
+ @Override
+ ModelSpec<T> resolved(List<Object> parameters);
+
+ /**
+ * Return the model's path
+ *
+ * @return path
+ */
+ ZPath path();
+
+ /**
+ * Return the model's serializer
+ *
+ * @return serializer
+ */
+ ModelSerializer<T> serializer();
+
+ /**
+ * Return the model's create mode
+ *
+ * @return create mode
+ */
+ CreateMode createMode();
+
+ /**
+ * Return the model's ACL list
+ *
+ * @return ACL list
+ */
+ List<ACL> aclList();
+
+ /**
+ * Return the model's create options
+ *
+ * @return create options
+ */
+ Set<CreateOption> createOptions();
+
+ /**
+ * Return the model's delete options
+ *
+ * @return delete options
+ */
+ Set<DeleteOption> deleteOptions();
+
+ /**
+ * Return the TTL to use or -1
+ *
+ * @return ttl
+ */
+ long ttl();
+
+ /**
+ * Return a Curator schema that validates ZNodes at this model's
+ * path using this model's values
+ *
+ * @return schema
+ */
+ Schema schema();
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpecBuilder.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpecBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpecBuilder.java
new file mode 100644
index 0000000..f6a2a51
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpecBuilder.java
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import org.apache.curator.x.async.modeled.details.ModelSpecImpl;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.ACL;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+public class ModelSpecBuilder<T>
+{
+ private final ModelSerializer<T> serializer;
+ private ZPath path;
+ private CreateMode createMode = CreateMode.PERSISTENT;
+ private List<ACL> aclList = Collections.emptyList();
+ private Set<CreateOption> createOptions = Collections.emptySet();
+ private Set<DeleteOption> deleteOptions = Collections.emptySet();
+ private long ttl = -1;
+
+ /**
+ * Build a new ModelSpec instance
+ *
+ * @return new ModelSpec instance
+ */
+ public ModelSpec<T> build()
+ {
+ return new ModelSpecImpl<>(path, serializer, createMode, aclList, createOptions, deleteOptions, ttl);
+ }
+
+ /**
+ * Use the given createMode for create operations on the Modeled Curator's ZNode
+ *
+ * @param createMode create mode
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withCreateMode(CreateMode createMode)
+ {
+ this.createMode = createMode;
+ return this;
+ }
+
+ /**
+ * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or
+ * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If
+ * the znode has not been modified within the given TTL, it will be deleted once it has no
+ * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to
+ * EphemeralType.MAX_TTL.
+ *
+ * @param ttl the ttl
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withTtl(long ttl)
+ {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * Use the given aclList for create operations on the Modeled Curator's ZNode
+ *
+ * @param aclList ACLs
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withAclList(List<ACL> aclList)
+ {
+ this.aclList = aclList;
+ return this;
+ }
+
+ /**
+ * Use the given create options on the Modeled Curator's ZNode
+ *
+ * @param createOptions options
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withCreateOptions(Set<CreateOption> createOptions)
+ {
+ this.createOptions = (createOptions != null) ? ImmutableSet.copyOf(createOptions) : null;
+ return this;
+ }
+
+ /**
+ * Use the given delete options on the Modeled Curator's ZNode
+ *
+ * @param deleteOptions options
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withDeleteOptions(Set<DeleteOption> deleteOptions)
+ {
+ this.deleteOptions = (deleteOptions != null) ? ImmutableSet.copyOf(deleteOptions) : null;
+ return this;
+ }
+
+ /**
+ * Change the model spec's path
+ *
+ * @param path new path
+ * @return this for chaining
+ */
+ public ModelSpecBuilder<T> withPath(ZPath path)
+ {
+ this.path = Objects.requireNonNull(path, "path cannot be null");
+ return this;
+ }
+
+ ModelSpecBuilder(ModelSerializer<T> serializer)
+ {
+ this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null");
+ }
+
+ ModelSpecBuilder(ZPath path, ModelSerializer<T> serializer)
+ {
+ this.path = Objects.requireNonNull(path, "path cannot be null");
+ this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null");
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java
new file mode 100644
index 0000000..8f03387
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java
@@ -0,0 +1,371 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework;
+import org.apache.zookeeper.data.Stat;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+public interface ModeledFramework<T>
+{
+ /**
+ * Return a new ModeledFramework for the given model
+ *
+ * @param client Curator client
+ * @param model the model
+ * @return new Modeled Curator instance
+ */
+ static <T> ModeledFramework<T> wrap(AsyncCuratorFramework client, ModelSpec<T> model)
+ {
+ return builder(client, model).build();
+ }
+
+ /**
+ * Start a new ModeledFrameworkBuilder for the given model
+ *
+ * @param client Curator client
+ * @param model the model
+ * @return builder
+ */
+ static <T> ModeledFrameworkBuilder<T> builder(AsyncCuratorFramework client, ModelSpec<T> model)
+ {
+ return new ModeledFrameworkBuilder<>(client, model);
+ }
+
+ /**
+ * Start a new ModeledFrameworkBuilder. A client and model must be provided prior to the instance
+ * being built via {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#withClient(org.apache.curator.x.async.AsyncCuratorFramework)}
+ * and {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#withModelSpec(ModelSpec)}
+ *
+ * @return builder
+ */
+ static <T> ModeledFrameworkBuilder<T> builder()
+ {
+ return new ModeledFrameworkBuilder<>();
+ }
+
+ /**
+ * <p>
+ * Use an internally created cache as a front for this modeled instance. All read APIs use the internal
+ * cache. i.e. read calls always use the cache instead of making direct queries. Note: you must call
+ * {@link org.apache.curator.x.async.modeled.cached.CachedModeledFramework#start()} and
+ * {@link org.apache.curator.x.async.modeled.cached.CachedModeledFramework#close()} to start/stop
+ * </p>
+ *
+ * <p>
+ * Note: the root node (the main path of the model) is <em>not</em> cached. i.e. only nodes
+ * below the root are cached.
+ * </p>
+ *
+ * <p>
+ * Note: this method internally allocates an Executor for the cache and read methods. Use
+ * {@link #cached(java.util.concurrent.ExecutorService)} if you'd like to provide your own executor service.
+ * </p>
+ *
+ * @return wrapped instance
+ */
+ CachedModeledFramework<T> cached();
+
+ /**
+ * Same as {@link #cached()} but allows for providing an executor service
+ *
+ * @param executor thread pool to use for the cache and for read operations
+ * @return wrapped instance
+ */
+ CachedModeledFramework<T> cached(ExecutorService executor);
+
+ /**
+ * Return mutator APIs that work with {@link org.apache.curator.x.async.modeled.versioned.Versioned} containers
+ *
+ * @return wrapped instance
+ */
+ VersionedModeledFramework<T> versioned();
+
+ /**
+ * Returns the client that was originally passed to {@link #wrap(org.apache.curator.x.async.AsyncCuratorFramework, ModelSpec)} or
+ * the builder.
+ *
+ * @return original client
+ */
+ AsyncCuratorFramework unwrap();
+
+ /**
+ * Return the model being used
+ *
+ * @return model
+ */
+ ModelSpec<T> modelSpec();
+
+ /**
+ * <p>
+ * Return a new Modeled Curator instance with all the same options but applying to the given child node of this Modeled Curator's
+ * path. E.g. if this Modeled Curator instance applies to "/a/b", calling <code>modeled.at("c")</code> returns an instance that applies to
+ * "/a/b/c".
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of child or,
+ * if it implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param child child node.
+ * @return new Modeled Curator instance
+ */
+ ModeledFramework<T> child(Object child);
+
+ /**
+ * <p>
+ * Return a new Modeled Curator instance with all the same options but applying to the parent node of this Modeled Curator's
+ * path. E.g. if this Modeled Curator instance applies to "/a/b/c", calling <code>modeled.parent()</code> returns an instance that applies to
+ * "/a/b".
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of child or,
+ * if it implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @return new Modeled Curator instance
+ */
+ ModeledFramework<T> parent();
+
+ /**
+ * Return a Modeled Curator instance with all the same options but using the given path.
+ *
+ * @param path new path
+ * @return new Modeled Curator instance
+ */
+ ModeledFramework<T> withPath(ZPath path);
+
+ /**
+ * Create (or update depending on build options) a ZNode at this instance's path with a serialized
+ * version of the given model
+ *
+ * @param model model to write
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<String> set(T model);
+
+ /**
+ * Create (or update depending on build options) a ZNode at this instance's path with a serialized
+ * version of the given model
+ *
+ * @param model model to write
+ * @param version if data is being set instead of creating the node, the data version to use
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<String> set(T model, int version);
+
+ /**
+ * Create (or update depending on build options) a ZNode at this instance's path with a serialized
+ * form of the given model
+ *
+ * @param model model to write
+ * @param storingStatIn the stat for the new ZNode is stored here
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<String> set(T model, Stat storingStatIn);
+
+ /**
+ * Create (or update depending on build options) a ZNode at this instance's path with a serialized
+ * form of the given model
+ *
+ * @param model model to write
+ * @param version if data is being set instead of creating the node, the data version to use
+ * @param storingStatIn the stat for the new ZNode is stored here
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<String> set(T model, Stat storingStatIn, int version);
+
+ /**
+ * Read the ZNode at this instance's path and deserialize into a model
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<T> read();
+
+ /**
+ * Read the ZNode at this instance's path and deserialize into a model
+ *
+ * @param storingStatIn the stat for the new ZNode is stored here
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<T> read(Stat storingStatIn);
+
+ /**
+ * Read the ZNode at this instance's path and deserialize into a model
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<ZNode<T>> readAsZNode();
+
+ /**
+ * Update the ZNode at this instance's path with a serialized
+ * form of the given model passing "-1" for the update version
+ *
+ * @param model model to write
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<Stat> update(T model);
+
+ /**
+ * Update the ZNode at this instance's path with a serialized
+ * form of the given model passing the given update version
+ *
+ * @param model model to write
+ * @param version update version to use
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<Stat> update(T model, int version);
+
+ /**
+ * Delete the ZNode at this instance's path passing -1 for the delete version
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<Void> delete();
+
+ /**
+ * Delete the ZNode at this instance's path passing the given delete version
+ *
+ * @param version update version to use
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<Void> delete(int version);
+
+ /**
+ * Check to see if the ZNode at this instance's path exists
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<Stat> checkExists();
+
+ /**
+ * Return the child paths of this instance's path (in no particular order)
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<List<ZPath>> children();
+
+ /**
+ * Return the child paths of this instance's path (in no particular order)
+ * and deserialize into a models. IMPORTANT: this results in a ZooKeeper query
+ * for each child node returned. i.e. if the initial children() call returns
+ * 10 nodes an additional 10 ZooKeeper queries are made to get the data. Note:
+ * cannot be used if any of the {@link ModeledFrameworkBuilder#watched()} modes
+ * are used.
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<List<ZNode<T>>> childrenAsZNodes();
+
+ /**
+ * Create operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction. Note:
+ * due to ZooKeeper transaction limits, this is a _not_ a "set or update" operation but only
+ * a create operation and will generate an error if the node already exists.
+ *
+ * @param model the model
+ * @return operation
+ */
+ CuratorOp createOp(T model);
+
+ /**
+ * Update operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @param model the model
+ * @return operation
+ */
+ CuratorOp updateOp(T model);
+
+ /**
+ * Create operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @param model the model
+ * @param version update version to use
+ * @return operation
+ */
+ CuratorOp updateOp(T model, int version);
+
+ /**
+ * Delete operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @return operation
+ */
+ CuratorOp deleteOp();
+
+ /**
+ * Delete operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @param version delete version to use
+ * @return operation
+ */
+ CuratorOp deleteOp(int version);
+
+ /**
+ * Check exists operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @return operation
+ */
+ CuratorOp checkExistsOp();
+
+ /**
+ * Check exists operation instance that can be passed among other operations to
+ * {@link #inTransaction(java.util.List)} to be executed as a single transaction.
+ *
+ * @param version version to use
+ * @return operation
+ */
+ CuratorOp checkExistsOp(int version);
+
+ /**
+ * Invoke ZooKeeper to commit the given operations as a single transaction.
+ *
+ * @param operations operations that make up the transaction.
+ * @return AsyncStage instance for managing the completion
+ */
+ AsyncStage<List<CuratorTransactionResult>> inTransaction(List<CuratorOp> operations);
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFrameworkBuilder.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFrameworkBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFrameworkBuilder.java
new file mode 100644
index 0000000..2e8bec3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFrameworkBuilder.java
@@ -0,0 +1,154 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.curator.x.async.modeled.details.ModeledFrameworkImpl;
+import org.apache.zookeeper.WatchedEvent;
+import java.util.Objects;
+import java.util.function.UnaryOperator;
+
+public class ModeledFrameworkBuilder<T>
+{
+ private AsyncCuratorFramework client;
+ private ModelSpec<T> modelSpec;
+ private WatchMode watchMode;
+ private UnaryOperator<WatchedEvent> watcherFilter;
+ private UnhandledErrorListener unhandledErrorListener;
+ private UnaryOperator<CuratorEvent> resultFilter;
+
+ /**
+ * Build a new ModeledFramework instance
+ *
+ * @return new ModeledFramework instance
+ */
+ public ModeledFramework<T> build()
+ {
+ return ModeledFrameworkImpl.build(
+ client,
+ modelSpec,
+ watchMode,
+ watcherFilter,
+ unhandledErrorListener,
+ resultFilter
+ );
+ }
+
+ /**
+ * Add watchers as appropriate to the Modeled Curator's ZNode using
+ * {@link org.apache.curator.x.async.WatchMode#stateChangeAndSuccess}
+ *
+ * @return this for chaining
+ * @see org.apache.curator.x.async.AsyncStage#event()
+ */
+ public ModeledFrameworkBuilder<T> watched()
+ {
+ this.watchMode = WatchMode.stateChangeAndSuccess;
+ return this;
+ }
+
+ /**
+ * Add watchers as appropriate using the given watchMode to the Modeled Curator's ZNode
+ *
+ * @param watchMode watcher style
+ * @return this for chaining
+ * @see org.apache.curator.x.async.AsyncStage#event()
+ */
+ public ModeledFrameworkBuilder<T> watched(WatchMode watchMode)
+ {
+ this.watchMode = watchMode;
+ return this;
+ }
+
+ /**
+ * Add watchers as appropriate using the given watchMode and filter to the Modeled Curator's ZNode
+ *
+ * @param watchMode watcher style
+ * @param watcherFilter filter
+ * @return this for chaining
+ * @see org.apache.curator.x.async.AsyncStage#event()
+ */
+ public ModeledFrameworkBuilder<T> watched(WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter)
+ {
+ this.watchMode = watchMode;
+ this.watcherFilter = watcherFilter;
+ return this;
+ }
+
+ /**
+ * Use the given unhandledErrorListener for operations on the Modeled Curator's ZNode
+ *
+ * @param unhandledErrorListener listener
+ * @return this for chaining
+ */
+ public ModeledFrameworkBuilder<T> withUnhandledErrorListener(UnhandledErrorListener unhandledErrorListener)
+ {
+ this.unhandledErrorListener = unhandledErrorListener;
+ return this;
+ }
+
+ /**
+ * Use the given result filter for operations on the Modeled Curator's ZNode
+ *
+ * @param resultFilter filter
+ * @return this for chaining
+ */
+ public ModeledFrameworkBuilder<T> withResultFilter(UnaryOperator<CuratorEvent> resultFilter)
+ {
+ this.resultFilter = resultFilter;
+ return this;
+ }
+
+ /**
+ * Change the model spec to use
+ *
+ * @param modelSpec model spec
+ * @return this for chaining
+ */
+ public ModeledFrameworkBuilder<T> withModelSpec(ModelSpec<T> modelSpec)
+ {
+ this.modelSpec = Objects.requireNonNull(modelSpec, "modelSpec cannot be null");
+ return this;
+ }
+
+ /**
+ * Change the client to use
+ *
+ * @param client new client
+ * @return this for chaining
+ */
+ public ModeledFrameworkBuilder<T> withClient(AsyncCuratorFramework client)
+ {
+ this.client = Objects.requireNonNull(client, "client cannot be null");
+ return this;
+ }
+
+ ModeledFrameworkBuilder()
+ {
+ }
+
+ ModeledFrameworkBuilder(AsyncCuratorFramework client, ModelSpec<T> modelSpec)
+ {
+ this.client = Objects.requireNonNull(client, "client cannot be null");
+ this.modelSpec = Objects.requireNonNull(modelSpec, "modelSpec cannot be null");
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/NodeName.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/NodeName.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/NodeName.java
new file mode 100644
index 0000000..6bc3be3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/NodeName.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+/**
+ * Used by the various "resolved" methods and "at" methods.
+ * If the argument to one of these methods implements this interface,
+ * the {@link #nodeName()} method is used instead of calling <code>toString()</code>
+ */
+@FunctionalInterface
+public interface NodeName
+{
+ String nodeName();
+
+ static String nameFrom(Object obj)
+ {
+ if ( obj instanceof NodeName )
+ {
+ return ((NodeName)obj).nodeName();
+ }
+ return String.valueOf(obj);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/Resolvable.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/Resolvable.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/Resolvable.java
new file mode 100644
index 0000000..209ffa1
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/Resolvable.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import java.util.Arrays;
+import java.util.List;
+
+public interface Resolvable
+{
+ /**
+ * When creating paths, any node in the path can be set to {@link ZPath#parameter()}.
+ * At runtime, the ZPath can be "resolved" by replacing these nodes with values.
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ZPath
+ */
+ default Object resolved(Object... parameters)
+ {
+ return resolved(Arrays.asList(parameters));
+ }
+
+ /**
+ * When creating paths, any node in the path can be set to {@link ZPath#parameter()}.
+ * At runtime, the ZPath can be "resolved" by replacing these nodes with values.
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ZPath
+ */
+ Object resolved(List<Object> parameters);
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZNode.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZNode.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZNode.java
new file mode 100644
index 0000000..0d34d82
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZNode.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.zookeeper.data.Stat;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.stream.Collectors;
+
+/**
+ * Abstracts a ZooKeeper node
+ */
+public interface ZNode<T>
+{
+ /**
+ * The path of the node
+ *
+ * @return path
+ */
+ ZPath path();
+
+ /**
+ * The node's last known stat if available
+ *
+ * @return stat
+ */
+ Stat stat();
+
+ /**
+ * The node's current model
+ *
+ * @return model
+ */
+ T model();
+
+ /**
+ * Utility that modifies an async stage of znodes into an async stage of models
+ *
+ * @param from original stage
+ * @return stage of models
+ */
+ static <T> CompletionStage<List<T>> models(AsyncStage<List<ZNode<T>>> from)
+ {
+ return from.thenApply(nodes -> nodes.stream().map(ZNode::model).collect(Collectors.toList()));
+ }
+
+ /**
+ * Utility that modifies an async stage of a znode into an async stage of a model
+ *
+ * @param from original stage
+ * @return stage of a model
+ */
+ static <T> CompletionStage<T> model(AsyncStage<ZNode<T>> from)
+ {
+ return from.thenApply(ZNode::model);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
new file mode 100644
index 0000000..70ac536
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
@@ -0,0 +1,279 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.x.async.modeled.details.ZPathImpl;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.UnaryOperator;
+import java.util.regex.Pattern;
+
+import static org.apache.curator.utils.ZKPaths.PATH_SEPARATOR;
+
+/**
+ * Abstracts a ZooKeeper ZNode path
+ */
+public interface ZPath extends Resolvable
+{
+ /**
+ * The root path: "/"
+ */
+ ZPath root = ZPathImpl.root;
+
+ /**
+ * Returns the special node name that can be used for replacements at runtime
+ * via {@link #resolved(Object...)} when passed via the various <code>from()</code> methods
+ */
+ static String parameter()
+ {
+ return parameter("id");
+ }
+
+ /**
+ * Same as {@link #parameter()} but allows you to specify an alternate code/name. This name
+ * has no effect and is only for debugging purposes. When <code>toString()</code> is called
+ * on ZPaths, this code shows.
+ */
+ static String parameter(String name)
+ {
+ return PATH_SEPARATOR + "{" + name + "}";
+ }
+
+ /**
+ * Take a string path and return a ZPath
+ *
+ * @param fullPath the path to parse
+ * @return ZPath
+ * @throws IllegalArgumentException if the path is invalid
+ */
+ static ZPath parse(String fullPath)
+ {
+ return ZPathImpl.parse(fullPath, s -> s);
+ }
+
+ /**
+ * Take a string path and return a ZPath. Each part of the path
+ * that is <code>{XXXX}</code> is replaced with {@link #parameter()}.
+ * E.g. <code>parseWithIds("/one/two/{first}/three/{second}")</code> is the equivalent
+ * of calling <code>ZPath.from("one", "two", parameter(), "three", parameter())</code>
+ *
+ * @param fullPath the path to parse
+ * @return ZPath
+ * @throws IllegalArgumentException if the path is invalid
+ */
+ static ZPath parseWithIds(String fullPath)
+ {
+ return ZPathImpl.parse(fullPath, s -> isId(s) ? (PATH_SEPARATOR + s) : s); // TODO
+ }
+
+ /**
+ * Return true if the given string conforms to the "{XXXX}" ID pattern
+ *
+ * @param s string to check
+ * @return true/false
+ */
+ static boolean isId(String s)
+ {
+ return s.startsWith("{") && s.endsWith("}");
+ }
+
+ /**
+ * Take a ZNode string path and return a ZPath
+ *
+ * @param fullPath the path to parse
+ * @param nameFilter each part of the path is passed through this filter
+ * @return ZPath
+ * @throws IllegalArgumentException if the path is invalid
+ */
+ static ZPath parse(String fullPath, UnaryOperator<String> nameFilter)
+ {
+ return ZPathImpl.parse(fullPath, nameFilter);
+ }
+
+ /**
+ * Convert individual path names into a ZPath. E.g.
+ * <code>ZPath.from("my", "full", "path")</code>. Any/all of the names can be passed as
+ * {@link #parameter()} so that the path can be resolved later using
+ * of the <code>resolved()</code> methods.
+ *
+ * @param names path names
+ * @return ZPath
+ * @throws IllegalArgumentException if any of the names is invalid
+ */
+ static ZPath from(String... names)
+ {
+ return ZPathImpl.from(names);
+ }
+
+ /**
+ * Convert individual path names into a ZPath. Any/all of the names can be passed as
+ * {@link #parameter()} so that the path can be resolved later using
+ * of the <code>resolved()</code> methods.
+ *
+ * @param names path names
+ * @return ZPath
+ * @throws IllegalArgumentException if any of the names is invalid
+ */
+ static ZPath from(List<String> names)
+ {
+ return ZPathImpl.from(names);
+ }
+
+ /**
+ * Convert individual path names into a ZPath starting at the given base. E.g.
+ * if base is "/home/base" <code>ZPath.from(base, "my", "full", "path")</code>
+ * would be "/home/base/my/full/path". Any/all of the names can be passed as
+ * {@link #parameter()} so that the path can be resolved later using
+ * of the <code>resolved()</code> methods.
+ *
+ * @param base base/starting path
+ * @param names path names
+ * @return ZPath
+ * @throws IllegalArgumentException if any of the names is invalid
+ */
+ static ZPath from(ZPath base, String... names)
+ {
+ return ZPathImpl.from(base, names);
+ }
+
+ /**
+ * Convert individual path names into a ZPath starting at the given base. Any/all of the names can be passed as
+ * {@link #parameter()} so that the path can be resolved later using
+ * of the <code>resolved()</code> methods.
+ *
+ * @param base base/starting path
+ * @param names path names
+ * @return ZPath
+ * @throws IllegalArgumentException if any of the names is invalid
+ */
+ static ZPath from(ZPath base, List<String> names)
+ {
+ return ZPathImpl.from(base, names);
+ }
+
+ /**
+ * <p>
+ * When creating paths, any node in the path can be set to {@link #parameter()}.
+ * At runtime, the ZPath can be "resolved" by replacing these nodes with values.
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of the parameter object or,
+ * if the object implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ZPath
+ */
+ @Override
+ default ZPath resolved(Object... parameters)
+ {
+ return resolved(Arrays.asList(parameters));
+ }
+
+ /**
+ * <p>
+ * When creating paths, any node in the path can be set to {@link #parameter()}.
+ * At runtime, the ZPath can be "resolved" by replacing these nodes with values.
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of the parameter object or,
+ * if the object implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param parameters list of replacements. Must have be the same length as the number of
+ * parameter nodes in the path
+ * @return new resolved ZPath
+ */
+ @Override
+ ZPath resolved(List<Object> parameters);
+
+ /**
+ * <p>
+ * Return a ZPath that represents a child ZNode of this ZPath. e.g.
+ * <code>ZPath.from("a", "b").at("c")</code> represents the path "/a/b/c"
+ * </p>
+ *
+ * <p>
+ * The replacement is the <code>toString()</code> value of child or,
+ * if it implements {@link org.apache.curator.x.async.modeled.NodeName},
+ * the value of <code>nodeName()</code>.
+ * </p>
+ *
+ * @param child child node name
+ * @return ZPath
+ */
+ ZPath child(Object child);
+
+ /**
+ * Return this ZPath's parent
+ *
+ * @return parent ZPath
+ * @throws java.util.NoSuchElementException if this is the root ZPath
+ */
+ ZPath parent();
+
+ /**
+ * Return true/false if this is the root ZPath
+ *
+ * @return true false
+ */
+ boolean isRoot();
+
+ /**
+ * Return true if this path is fully resolved (i.e. has no unresoled parameters)
+ *
+ * @return true/false
+ */
+ boolean isResolved();
+
+ /**
+ * Return true if this path starts with the given path. i.e.
+ * <code>ZPath.from("/one/two/three").startsWith(ZPath.from("/one/two"))</code> returns true
+ *
+ * @param path base path
+ * @return true/false
+ */
+ boolean startsWith(ZPath path);
+
+ /**
+ * The string full path that this ZPath represents
+ *
+ * @return full path
+ */
+ String fullPath();
+
+ /**
+ * The node name at this ZPath
+ *
+ * @return name
+ */
+ String nodeName();
+
+ /**
+ * Return a regex Pattern useful for using in {@link org.apache.curator.framework.schema.Schema}
+ *
+ * @return pattern for this path
+ */
+ Pattern toSchemaPathPattern();
+}