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(); +}
