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:33 UTC
[1/6] curator git commit: Squashed commit of the following:
Repository: curator
Updated Branches:
refs/heads/master 8b28b1208 -> 0f5d10da3
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-rpc/src/site/site.xml
----------------------------------------------------------------------
diff --git a/curator-x-rpc/src/site/site.xml b/curator-x-rpc/src/site/site.xml
index 135c902..fca1e73 100644
--- a/curator-x-rpc/src/site/site.xml
+++ b/curator-x-rpc/src/site/site.xml
@@ -25,7 +25,7 @@
<link rel="stylesheet" href="../css/site.css" />
<script type="text/javascript">
$(function(){
- $('a[title="Curator Async"]').parent().addClass("active");
+ $('a[title="Curator RPC Proxy"]').parent().addClass("active");
});
</script>
</head>
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 73346f9..aa8fd86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -364,6 +364,18 @@
</dependency>
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-x-async</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-x-rpc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math</artifactId>
<version>${commons-math-version}</version>
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/src/site/site.xml
----------------------------------------------------------------------
diff --git a/src/site/site.xml b/src/site/site.xml
index 222ffde..83ddd46 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -73,6 +73,8 @@
<item name="Framework" href="curator-framework/index.html"/>
<item name="Utilities" href="utilities.html"/>
<item name="Client" href="curator-client/index.html"/>
+ <item name="Java 8/Async" href="curator-x-async/index.html"/>
+ <item name="Strongly Typed Models" href="curator-x-async/modeled.html"/>
<item name="Schema Support" href="curator-framework/schema.html"/>
</menu>
@@ -94,7 +96,7 @@
<item name="Service Discovery" href="curator-x-discovery/index.html"/>
<item name="Service Discovery Server" href="curator-x-discovery-server/index.html"/>
<item name="Curator RPC Proxy" href="curator-x-rpc/index.html"/>
- <item name="Curator Async" href="curator-x-async/index.html"/>
+ <item name="Curator Java 8/Async" href="curator-x-async/index.html"/>
</menu>
<menu name="Community" inherit="top">
[4/6] curator git commit: Squashed commit of the following:
Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledFramework.java
new file mode 100644
index 0000000..8acbebb
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledFramework.java
@@ -0,0 +1,123 @@
+/**
+ * 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.cached;
+
+import org.apache.curator.framework.listen.Listenable;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.data.Stat;
+import java.io.Closeable;
+import java.util.List;
+
+public interface CachedModeledFramework<T> extends ModeledFramework<T>, Closeable
+{
+ /**
+ * Return the cache instance
+ *
+ * @return cache
+ */
+ ModeledCache<T> cache();
+
+ /**
+ * Returns a view of this instance that uses the CachedModeledFramework's executor
+ * for all default async completion operations. i.e. when you use, for example,
+ * {@link java.util.concurrent.CompletionStage#handleAsync(java.util.function.BiFunction)}
+ * this instance's executor is used instead of <code>ForkJoinPool.commonPool()</code>.
+ *
+ * @return view
+ */
+ CachedModeledFramework<T> asyncDefault();
+
+ /**
+ * Start the internally created cache
+ */
+ void start();
+
+ /**
+ * Close/stop the internally created cache
+ */
+ @Override
+ void close();
+
+ /**
+ * Return the listener container so that you can add/remove listeners
+ *
+ * @return listener container
+ */
+ Listenable<ModeledCacheListener<T>> listenable();
+
+ /**
+ * Same as {@link org.apache.curator.x.async.modeled.ModeledFramework#childrenAsZNodes()}
+ * but always reads from cache - i.e. no additional queries to ZooKeeper are made
+ *
+ * @return AsyncStage stage
+ */
+ @Override
+ AsyncStage<List<ZNode<T>>> childrenAsZNodes();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ CachedModeledFramework<T> child(Object child);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ CachedModeledFramework<T> withPath(ZPath path);
+
+ /**
+ * Same as {@link #read()} except that if the cache does not have a value
+ * for this path a direct query is made.
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<T> readThrough();
+
+ /**
+ * Same as {@link #read(org.apache.zookeeper.data.Stat)} except that if the cache does not have a value
+ * for this path a direct query is made.
+ *
+ * @param storingStatIn the stat for the new ZNode is stored here
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<T> readThrough(Stat storingStatIn);
+
+ /**
+ * Same as {@link #readAsZNode()} except that if the cache does not have a value
+ * for this path a direct query is made.
+ *
+ *
+ * @return AsyncStage
+ * @see org.apache.curator.x.async.AsyncStage
+ */
+ AsyncStage<ZNode<T>> readThroughAsZNode();
+
+ /**
+ * Return the instances of the base path of this cached framework
+ *
+ * @return listing of all models in the base path
+ */
+ AsyncStage<List<T>> list();
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCache.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCache.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCache.java
new file mode 100644
index 0000000..6677268
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCache.java
@@ -0,0 +1,46 @@
+/**
+ * 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.cached;
+
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.curator.x.async.modeled.ZPath;
+import java.util.Map;
+import java.util.Optional;
+
+public interface ModeledCache<T>
+{
+ /**
+ * Return the modeled current data for the given path. There are no guarantees of accuracy. This is
+ * merely the most recent view of the data. If there is no node at the given path,
+ * {@link java.util.Optional#empty()} is returned.
+ *
+ * @param path path to the node to check
+ * @return data if the node is alive, or empty
+ */
+ Optional<ZNode<T>> currentData(ZPath path);
+
+ /**
+ * Return the modeled current set of children at the given path, mapped by child name. There are no
+ * guarantees of accuracy; this is merely the most recent view of the data.
+ *
+ * @param path path to the node to check
+ * @return a possibly-empty map of children if the node is alive
+ */
+ Map<ZPath, ZNode<T>> currentChildren(ZPath path);
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCacheListener.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCacheListener.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCacheListener.java
new file mode 100644
index 0000000..42498c0
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCacheListener.java
@@ -0,0 +1,106 @@
+/**
+ * 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.cached;
+
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.LoggerFactory;
+
+@FunctionalInterface
+public interface ModeledCacheListener<T>
+{
+ enum Type
+ {
+ /**
+ * A child was added to the path
+ */
+ NODE_ADDED,
+
+ /**
+ * A child's data was changed
+ */
+ NODE_UPDATED,
+
+ /**
+ * A child was removed from the path
+ */
+ NODE_REMOVED
+ }
+
+ /**
+ * The given path was added, updated or removed
+ *
+ * @param type action type
+ * @param path the path
+ * @param stat the node's stat (previous stat for removal)
+ * @param model the node's model (previous model for removal)
+ */
+ void accept(Type type, ZPath path, Stat stat, T model);
+
+ /**
+ * The cache has finished initializing
+ */
+ default void initialized()
+ {
+ // NOP
+ }
+
+ /**
+ * Called when there is an exception processing a message from the internal cache. This is most
+ * likely due to a de-serialization problem.
+ *
+ * @param e the exception
+ */
+ default void handleException(Exception e)
+ {
+ LoggerFactory.getLogger(getClass()).error("Could not process cache message", e);
+ }
+
+ /**
+ * Returns a version of this listener that only begins calling
+ * {@link #accept(org.apache.curator.x.async.modeled.cached.ModeledCacheListener.Type, org.apache.curator.x.async.modeled.ZPath, org.apache.zookeeper.data.Stat, Object)}
+ * once {@link #initialized()} has been called. i.e. changes that occur as the cache is initializing are not sent
+ * to the listener
+ *
+ * @return wrapped listener
+ */
+ default ModeledCacheListener<T> postInitializedOnly()
+ {
+ return new ModeledCacheListener<T>()
+ {
+ private volatile boolean isInitialized = false;
+
+ @Override
+ public void accept(Type type, ZPath path, Stat stat, T model)
+ {
+ if ( isInitialized )
+ {
+ ModeledCacheListener.this.accept(type, path, stat, model);
+ }
+ }
+
+ @Override
+ public void initialized()
+ {
+ isInitialized = true;
+ ModeledCacheListener.this.initialized();
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java
new file mode 100644
index 0000000..2a7fd5f
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java
@@ -0,0 +1,342 @@
+/**
+ * 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.details;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.framework.listen.Listenable;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.cached.ModeledCache;
+import org.apache.curator.x.async.modeled.cached.ModeledCacheListener;
+import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.DataTree;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+class CachedModeledFrameworkImpl<T> implements CachedModeledFramework<T>
+{
+ private final ModeledFramework<T> client;
+ private final ModeledCacheImpl<T> cache;
+ private final Executor executor;
+ private final boolean asyncDefaultMode;
+
+ CachedModeledFrameworkImpl(ModeledFramework<T> client, ExecutorService executor)
+ {
+ this(client, new ModeledCacheImpl<>(client.unwrap().unwrap(), client.modelSpec(), executor), executor, false);
+ }
+
+ private CachedModeledFrameworkImpl(ModeledFramework<T> client, ModeledCacheImpl<T> cache, Executor executor, boolean asyncDefaultMode)
+ {
+ this.client = client;
+ this.cache = cache;
+ this.executor = executor;
+ this.asyncDefaultMode = asyncDefaultMode;
+ }
+
+ @Override
+ public ModeledCache<T> cache()
+ {
+ return cache;
+ }
+
+ @Override
+ public CachedModeledFramework<T> asyncDefault()
+ {
+ return new CachedModeledFrameworkImpl<>(client, cache, executor, true);
+ }
+
+ @Override
+ public void start()
+ {
+ cache.start();
+ }
+
+ @Override
+ public void close()
+ {
+ cache.close();
+ }
+
+ @Override
+ public Listenable<ModeledCacheListener<T>> listenable()
+ {
+ return cache.listenable();
+ }
+
+ @Override
+ public CachedModeledFramework<T> cached()
+ {
+ throw new UnsupportedOperationException("Already a cached instance");
+ }
+
+ @Override
+ public CachedModeledFramework<T> cached(ExecutorService executor)
+ {
+ throw new UnsupportedOperationException("Already a cached instance");
+ }
+
+ @Override
+ public VersionedModeledFramework<T> versioned()
+ {
+ return new VersionedModeledFrameworkImpl<>(this);
+ }
+
+ @Override
+ public AsyncCuratorFramework unwrap()
+ {
+ return client.unwrap();
+ }
+
+ @Override
+ public ModelSpec<T> modelSpec()
+ {
+ return client.modelSpec();
+ }
+
+ @Override
+ public CachedModeledFramework<T> child(Object child)
+ {
+ return new CachedModeledFrameworkImpl<>(client.child(child), cache, executor, asyncDefaultMode);
+ }
+
+ @Override
+ public ModeledFramework<T> parent()
+ {
+ throw new UnsupportedOperationException("Not supported for CachedModeledFramework. Instead, call parent() on the ModeledFramework before calling cached()");
+ }
+
+ @Override
+ public CachedModeledFramework<T> withPath(ZPath path)
+ {
+ return new CachedModeledFrameworkImpl<>(client.withPath(path), cache, executor, asyncDefaultMode);
+ }
+
+ @Override
+ public AsyncStage<String> set(T model)
+ {
+ return client.set(model);
+ }
+
+ @Override
+ public AsyncStage<String> set(T model, Stat storingStatIn)
+ {
+ return client.set(model, storingStatIn);
+ }
+
+ @Override
+ public AsyncStage<String> set(T model, Stat storingStatIn, int version)
+ {
+ return client.set(model, storingStatIn, version);
+ }
+
+ @Override
+ public AsyncStage<String> set(T model, int version)
+ {
+ return client.set(model, version);
+ }
+
+ @Override
+ public AsyncStage<T> read()
+ {
+ return internalRead(ZNode::model, this::exceptionally);
+ }
+
+ @Override
+ public AsyncStage<T> read(Stat storingStatIn)
+ {
+ return internalRead(n -> {
+ if ( storingStatIn != null )
+ {
+ DataTree.copyStat(n.stat(), storingStatIn);
+ }
+ return n.model();
+ }, this::exceptionally);
+ }
+
+ @Override
+ public AsyncStage<ZNode<T>> readAsZNode()
+ {
+ return internalRead(Function.identity(), this::exceptionally);
+ }
+
+ @Override
+ public AsyncStage<T> readThrough()
+ {
+ return internalRead(ZNode::model, client::read);
+ }
+
+ @Override
+ public AsyncStage<T> readThrough(Stat storingStatIn)
+ {
+ return internalRead(ZNode::model, () -> client.read(storingStatIn));
+ }
+
+ @Override
+ public AsyncStage<ZNode<T>> readThroughAsZNode()
+ {
+ return internalRead(Function.identity(), client::readAsZNode);
+ }
+
+ @Override
+ public AsyncStage<List<T>> list()
+ {
+ List<T> children = cache.currentChildren()
+ .values()
+ .stream()
+ .map(ZNode::model)
+ .collect(Collectors.toList());
+ return asyncDefaultMode ? ModelStage.asyncCompleted(children, executor) : ModelStage.completed(children);
+ }
+
+ @Override
+ public AsyncStage<Stat> update(T model)
+ {
+ return client.update(model);
+ }
+
+ @Override
+ public AsyncStage<Stat> update(T model, int version)
+ {
+ return client.update(model, version);
+ }
+
+ @Override
+ public AsyncStage<Void> delete()
+ {
+ return client.delete();
+ }
+
+ @Override
+ public AsyncStage<Void> delete(int version)
+ {
+ return client.delete(version);
+ }
+
+ @Override
+ public AsyncStage<Stat> checkExists()
+ {
+ ZPath path = client.modelSpec().path();
+ Optional<ZNode<T>> data = cache.currentData(path);
+ return data.map(node -> completed(node.stat())).orElseGet(() -> completed(null));
+ }
+
+ @Override
+ public AsyncStage<List<ZPath>> children()
+ {
+ List<ZPath> paths = cache.currentChildren(client.modelSpec().path())
+ .keySet()
+ .stream()
+ .filter(path -> path.equals(cache.basePath()))
+ .collect(Collectors.toList());
+ return completed(paths);
+ }
+
+ @Override
+ public AsyncStage<List<ZNode<T>>> childrenAsZNodes()
+ {
+ List<ZNode<T>> nodes = cache.currentChildren(client.modelSpec().path())
+ .entrySet()
+ .stream()
+ .filter(e -> e.getKey().equals(cache.basePath()))
+ .map(Map.Entry::getValue)
+ .collect(Collectors.toList());
+ return completed(nodes);
+ }
+
+ @Override
+ public CuratorOp createOp(T model)
+ {
+ return client.createOp(model);
+ }
+
+ @Override
+ public CuratorOp updateOp(T model)
+ {
+ return client.updateOp(model);
+ }
+
+ @Override
+ public CuratorOp updateOp(T model, int version)
+ {
+ return client.updateOp(model, version);
+ }
+
+ @Override
+ public CuratorOp deleteOp()
+ {
+ return client.deleteOp();
+ }
+
+ @Override
+ public CuratorOp deleteOp(int version)
+ {
+ return client.deleteOp(version);
+ }
+
+ @Override
+ public CuratorOp checkExistsOp()
+ {
+ return client.checkExistsOp();
+ }
+
+ @Override
+ public CuratorOp checkExistsOp(int version)
+ {
+ return client.checkExistsOp(version);
+ }
+
+ @Override
+ public AsyncStage<List<CuratorTransactionResult>> inTransaction(List<CuratorOp> operations)
+ {
+ return client.inTransaction(operations);
+ }
+
+ private <U> AsyncStage<U> completed(U value)
+ {
+ return asyncDefaultMode ? ModelStage.asyncCompleted(value, executor) : ModelStage.completed(value);
+ }
+
+ private <U> AsyncStage<U> exceptionally()
+ {
+ KeeperException.NoNodeException exception = new KeeperException.NoNodeException(client.modelSpec().path().fullPath());
+ return asyncDefaultMode ? ModelStage.asyncExceptionally(exception, executor) : ModelStage.exceptionally(exception);
+ }
+
+ private <U> AsyncStage<U> internalRead(Function<ZNode<T>, U> resolver, Supplier<AsyncStage<U>> elseProc)
+ {
+ ZPath path = client.modelSpec().path();
+ Optional<ZNode<T>> data = cache.currentData(path);
+ return data.map(node -> completed(resolver.apply(node)))
+ .orElseGet(elseProc);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java
new file mode 100644
index 0000000..58405eb
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java
@@ -0,0 +1,239 @@
+/**
+ * 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.details;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.curator.framework.schema.Schema;
+import org.apache.curator.framework.schema.SchemaValidator;
+import org.apache.curator.framework.schema.SchemaViolation;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import org.apache.curator.x.async.modeled.ModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+public class ModelSpecImpl<T> implements ModelSpec<T>, SchemaValidator
+{
+ private final ZPath path;
+ private final ModelSerializer<T> serializer;
+ private final CreateMode createMode;
+ private final List<ACL> aclList;
+ private final Set<CreateOption> createOptions;
+ private final Set<DeleteOption> deleteOptions;
+ private final long ttl;
+ private volatile Schema schema = null;
+
+ public ModelSpecImpl(ZPath path, ModelSerializer<T> serializer, CreateMode createMode, List<ACL> aclList, Set<CreateOption> createOptions, Set<DeleteOption> deleteOptions, long ttl)
+ {
+ this.path = Objects.requireNonNull(path, "path cannot be null");
+ this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null");
+ this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null");
+ this.aclList = ImmutableList.copyOf(Objects.requireNonNull(aclList, "aclList cannot be null"));
+ this.createOptions = ImmutableSet.copyOf(Objects.requireNonNull(createOptions, "createOptions cannot be null"));
+ this.deleteOptions = ImmutableSet.copyOf(Objects.requireNonNull(deleteOptions, "deleteOptions cannot be null"));
+ this.ttl = ttl;
+ }
+
+ @Override
+ public ModelSpec<T> child(Object child)
+ {
+ return withPath(path.child(child));
+ }
+
+ @Override
+ public ModelSpec<T> parent()
+ {
+ return withPath(path.parent());
+ }
+
+ @Override
+ public ModelSpec<T> resolved(Object... parameters)
+ {
+ return withPath(path.resolved(parameters));
+ }
+
+ @Override
+ public ModelSpec<T> resolved(List<Object> parameters)
+ {
+ return withPath(path.resolved(parameters));
+ }
+
+ @Override
+ public ModelSpec<T> withPath(ZPath newPath)
+ {
+ return new ModelSpecImpl<>(newPath, serializer, createMode, aclList, createOptions, deleteOptions, ttl);
+ }
+
+ @Override
+ public ZPath path()
+ {
+ return path;
+ }
+
+ @Override
+ public ModelSerializer<T> serializer()
+ {
+ return serializer;
+ }
+
+ @Override
+ public CreateMode createMode()
+ {
+ return createMode;
+ }
+
+ @Override
+ public List<ACL> aclList()
+ {
+ return aclList;
+ }
+
+ @Override
+ public Set<CreateOption> createOptions()
+ {
+ return createOptions;
+ }
+
+ @Override
+ public Set<DeleteOption> deleteOptions()
+ {
+ return deleteOptions;
+ }
+
+ @Override
+ public long ttl()
+ {
+ return ttl;
+ }
+
+ @Override
+ public Schema schema()
+ {
+ if ( schema == null )
+ {
+ schema = Schema.builder(path.toSchemaPathPattern())
+ .dataValidator(this)
+ .ephemeral(createMode.isEphemeral() ? Schema.Allowance.MUST : Schema.Allowance.CANNOT)
+ .canBeDeleted(true)
+ .sequential(createMode.isSequential() ? Schema.Allowance.MUST : Schema.Allowance.CANNOT)
+ .watched(Schema.Allowance.CAN)
+ .build();
+ }
+ return schema;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ ModelSpecImpl<?> modelSpec = (ModelSpecImpl<?>)o;
+
+ if ( ttl != modelSpec.ttl )
+ {
+ return false;
+ }
+ if ( !path.equals(modelSpec.path) )
+ {
+ return false;
+ }
+ if ( !serializer.equals(modelSpec.serializer) )
+ {
+ return false;
+ }
+ if ( createMode != modelSpec.createMode )
+ {
+ return false;
+ }
+ if ( !aclList.equals(modelSpec.aclList) )
+ {
+ return false;
+ }
+ if ( !createOptions.equals(modelSpec.createOptions) )
+ {
+ return false;
+ }
+ //noinspection SimplifiableIfStatement
+ if ( !deleteOptions.equals(modelSpec.deleteOptions) )
+ {
+ return false;
+ }
+ return schema.equals(modelSpec.schema);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = path.hashCode();
+ result = 31 * result + serializer.hashCode();
+ result = 31 * result + createMode.hashCode();
+ result = 31 * result + aclList.hashCode();
+ result = 31 * result + createOptions.hashCode();
+ result = 31 * result + deleteOptions.hashCode();
+ result = 31 * result + (int)(ttl ^ (ttl >>> 32));
+ result = 31 * result + schema.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "ModelSpecImpl{" + "path=" + path + ", serializer=" + serializer + ", createMode=" + createMode + ", aclList=" + aclList + ", createOptions=" + createOptions + ", deleteOptions=" + deleteOptions + ", ttl=" + ttl + ", schema=" + schema + '}';
+ }
+
+ @Override
+ public boolean isValid(Schema schema, String path, byte[] data, List<ACL> acl)
+ {
+ if ( acl != null )
+ {
+ List<ACL> localAclList = (aclList.size() > 0) ? aclList : ZooDefs.Ids.OPEN_ACL_UNSAFE;
+ if ( !acl.equals(localAclList) )
+ {
+ throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "ACLs do not match model ACLs");
+ }
+ }
+
+ if ( data != null )
+ {
+ try
+ {
+ serializer.deserialize(data);
+ }
+ catch ( RuntimeException e )
+ {
+ throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "Data cannot be deserialized into a model");
+ }
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
new file mode 100644
index 0000000..27047ec
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
@@ -0,0 +1,171 @@
+/**
+ * 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.details;
+
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.zookeeper.WatchedEvent;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+class ModelStage<T> extends CompletableFuture<T> implements AsyncStage<T>
+{
+ private final CompletionStage<WatchedEvent> event;
+
+ static <U> ModelStage<U> make()
+ {
+ return new ModelStage<>(null);
+ }
+
+ static <U> ModelStage<U> make(CompletionStage<WatchedEvent> event)
+ {
+ return new ModelStage<>(event);
+ }
+
+ static <U> ModelStage<U> completed(U value)
+ {
+ ModelStage<U> stage = new ModelStage<>(null);
+ stage.complete(value);
+ return stage;
+ }
+
+ static <U> ModelStage<U> exceptionally(Exception e)
+ {
+ ModelStage<U> stage = new ModelStage<>(null);
+ stage.completeExceptionally(e);
+ return stage;
+ }
+
+ static <U> ModelStage<U> async(Executor executor)
+ {
+ return new AsyncModelStage<>(executor);
+ }
+
+ static <U> ModelStage<U> asyncCompleted(U value, Executor executor)
+ {
+ ModelStage<U> stage = new AsyncModelStage<>(executor);
+ stage.complete(value);
+ return stage;
+ }
+
+ static <U> ModelStage<U> asyncExceptionally(Exception e, Executor executor)
+ {
+ ModelStage<U> stage = new AsyncModelStage<>(executor);
+ stage.completeExceptionally(e);
+ return stage;
+ }
+
+ @Override
+ public CompletionStage<WatchedEvent> event()
+ {
+ return event;
+ }
+
+ private ModelStage(CompletionStage<WatchedEvent> event)
+ {
+ this.event = event;
+ }
+
+ private static class AsyncModelStage<U> extends ModelStage<U>
+ {
+ private final Executor executor;
+
+ public AsyncModelStage(Executor executor)
+ {
+ super(null);
+ this.executor = executor;
+ }
+
+ @Override
+ public <U1> CompletableFuture<U1> thenApplyAsync(Function<? super U, ? extends U1> fn)
+ {
+ return super.thenApplyAsync(fn, executor);
+ }
+
+ @Override
+ public CompletableFuture<Void> thenAcceptAsync(Consumer<? super U> action)
+ {
+ return super.thenAcceptAsync(action, executor);
+ }
+
+ @Override
+ public CompletableFuture<Void> thenRunAsync(Runnable action)
+ {
+ return super.thenRunAsync(action, executor);
+ }
+
+ @Override
+ public <U1, V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U1> other, BiFunction<? super U, ? super U1, ? extends V> fn)
+ {
+ return super.thenCombineAsync(other, fn, executor);
+ }
+
+ @Override
+ public <U1> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U1> other, BiConsumer<? super U, ? super U1> action)
+ {
+ return super.thenAcceptBothAsync(other, action, executor);
+ }
+
+ @Override
+ public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
+ {
+ return super.runAfterBothAsync(other, action, executor);
+ }
+
+ @Override
+ public <U1> CompletableFuture<U1> applyToEitherAsync(CompletionStage<? extends U> other, Function<? super U, U1> fn)
+ {
+ return super.applyToEitherAsync(other, fn, executor);
+ }
+
+ @Override
+ public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends U> other, Consumer<? super U> action)
+ {
+ return super.acceptEitherAsync(other, action, executor);
+ }
+
+ @Override
+ public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
+ {
+ return super.runAfterEitherAsync(other, action, executor);
+ }
+
+ @Override
+ public <U1> CompletableFuture<U1> thenComposeAsync(Function<? super U, ? extends CompletionStage<U1>> fn)
+ {
+ return super.thenComposeAsync(fn, executor);
+ }
+
+ @Override
+ public CompletableFuture<U> whenCompleteAsync(BiConsumer<? super U, ? super Throwable> action)
+ {
+ return super.whenCompleteAsync(action, executor);
+ }
+
+ @Override
+ public <U1> CompletableFuture<U1> handleAsync(BiFunction<? super U, Throwable, ? extends U1> fn)
+ {
+ return super.handleAsync(fn, executor);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCacheImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCacheImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCacheImpl.java
new file mode 100644
index 0000000..72e6762
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCacheImpl.java
@@ -0,0 +1,211 @@
+/**
+ * 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.details;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.listen.Listenable;
+import org.apache.curator.framework.listen.ListenerContainer;
+import org.apache.curator.framework.recipes.cache.TreeCache;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
+import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+import org.apache.curator.utils.ThreadUtils;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.modeled.ModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.curator.x.async.modeled.cached.ModeledCache;
+import org.apache.curator.x.async.modeled.cached.ModeledCacheListener;
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.zookeeper.data.Stat;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+class ModeledCacheImpl<T> implements TreeCacheListener, ModeledCache<T>
+{
+ private final TreeCache cache;
+ private final Map<ZPath, Entry<T>> entries = new ConcurrentHashMap<>();
+ private final ModelSerializer<T> serializer;
+ private final ListenerContainer<ModeledCacheListener<T>> listenerContainer = new ListenerContainer<>();
+ private final ZPath basePath;
+
+ private static final class Entry<T>
+ {
+ final Stat stat;
+ final T model;
+
+ Entry(Stat stat, T model)
+ {
+ this.stat = stat;
+ this.model = model;
+ }
+ }
+
+ ModeledCacheImpl(CuratorFramework client, ModelSpec<T> modelSpec, ExecutorService executor)
+ {
+ if ( !modelSpec.path().isResolved() && !modelSpec.path().isRoot() && modelSpec.path().parent().isResolved() )
+ {
+ modelSpec = modelSpec.parent(); // i.e. the last item is a parameter
+ }
+
+ basePath = modelSpec.path();
+ this.serializer = modelSpec.serializer();
+ cache = TreeCache.newBuilder(client, basePath.fullPath())
+ .setCacheData(false)
+ .setDataIsCompressed(modelSpec.createOptions().contains(CreateOption.compress))
+ .setExecutor(executor)
+ .setCreateParentNodes(modelSpec.createOptions().contains(CreateOption.createParentsIfNeeded) || modelSpec.createOptions().contains(CreateOption.createParentsAsContainers))
+ .build();
+ }
+
+ public void start()
+ {
+ try
+ {
+ cache.getListenable().addListener(this);
+ cache.start();
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void close()
+ {
+ cache.getListenable().removeListener(this);
+ cache.close();
+ entries.clear();
+ }
+
+ @Override
+ public Optional<ZNode<T>> currentData(ZPath path)
+ {
+ Entry<T> entry = entries.remove(path);
+ if ( entry != null )
+ {
+ return Optional.of(new ZNodeImpl<>(path, entry.stat, entry.model));
+ }
+ return Optional.empty();
+ }
+
+ ZPath basePath()
+ {
+ return basePath;
+ }
+
+ Map<ZPath, ZNode<T>> currentChildren()
+ {
+ return currentChildren(basePath);
+ }
+
+ @Override
+ public Map<ZPath, ZNode<T>> currentChildren(ZPath path)
+ {
+ return entries.entrySet()
+ .stream()
+ .filter(entry -> entry.getKey().startsWith(path))
+ .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), new ZNodeImpl<>(entry.getKey(), entry.getValue().stat, entry.getValue().model)))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ public Listenable<ModeledCacheListener<T>> listenable()
+ {
+ return listenerContainer;
+ }
+
+ @Override
+ public void childEvent(CuratorFramework client, TreeCacheEvent event)
+ {
+ try
+ {
+ internalChildEvent(event);
+ }
+ catch ( Exception e )
+ {
+ ThreadUtils.checkInterrupted(e);
+
+ listenerContainer.forEach(l -> {
+ l.handleException(e);
+ return null;
+ });
+ }
+ }
+
+ private void internalChildEvent(TreeCacheEvent event) throws Exception
+ {
+ switch ( event.getType() )
+ {
+ case NODE_ADDED:
+ case NODE_UPDATED:
+ {
+ ZPath path = ZPath.parse(event.getData().getPath());
+ if ( !path.equals(basePath) )
+ {
+ byte[] bytes = event.getData().getData();
+ if ( (bytes != null) && (bytes.length > 0) ) // otherwise it's probably just a parent node being created
+ {
+ T model = serializer.deserialize(bytes);
+ entries.put(path, new Entry<>(event.getData().getStat(), model));
+ ModeledCacheListener.Type type = (event.getType() == TreeCacheEvent.Type.NODE_ADDED) ? ModeledCacheListener.Type.NODE_ADDED : ModeledCacheListener.Type.NODE_UPDATED;
+ accept(type, path, event.getData().getStat(), model);
+ }
+ }
+ break;
+ }
+
+ case NODE_REMOVED:
+ {
+ ZPath path = ZPath.parse(event.getData().getPath());
+ if ( !path.equals(basePath) )
+ {
+ Entry<T> entry = entries.remove(path);
+ T model = (entry != null) ? entry.model : serializer.deserialize(event.getData().getData());
+ Stat stat = (entry != null) ? entry.stat : event.getData().getStat();
+ accept(ModeledCacheListener.Type.NODE_REMOVED, path, stat, model);
+ }
+ break;
+ }
+
+ case INITIALIZED:
+ {
+ listenerContainer.forEach(l -> {
+ l.initialized();
+ return null;
+ });
+ break;
+ }
+
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ private void accept(ModeledCacheListener.Type type, ZPath path, Stat stat, T model)
+ {
+ listenerContainer.forEach(l -> {
+ l.accept(type, path, stat, model);
+ return null;
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java
new file mode 100644
index 0000000..c1d19c4
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java
@@ -0,0 +1,450 @@
+/**
+ * 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.details;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.utils.ThreadUtils;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.curator.x.async.api.AsyncCuratorFrameworkDsl;
+import org.apache.curator.x.async.api.AsyncPathAndBytesable;
+import org.apache.curator.x.async.api.AsyncPathable;
+import org.apache.curator.x.async.api.AsyncTransactionSetDataBuilder;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.WatchableAsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+
+public class ModeledFrameworkImpl<T> implements ModeledFramework<T>
+{
+ private final AsyncCuratorFramework client;
+ private final WatchableAsyncCuratorFramework watchableClient;
+ private final ModelSpec<T> modelSpec;
+ private final WatchMode watchMode;
+ private final UnaryOperator<WatchedEvent> watcherFilter;
+ private final UnhandledErrorListener unhandledErrorListener;
+ private final UnaryOperator<CuratorEvent> resultFilter;
+ private final AsyncCuratorFrameworkDsl dslClient;
+ private final boolean isWatched;
+
+ public static <T> ModeledFrameworkImpl<T> build(AsyncCuratorFramework client, ModelSpec<T> model, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter)
+ {
+ boolean isWatched = (watchMode != null);
+
+ Objects.requireNonNull(client, "client cannot be null");
+ Objects.requireNonNull(model, "model cannot be null");
+
+ watchMode = (watchMode != null) ? watchMode : WatchMode.stateChangeAndSuccess;
+
+ AsyncCuratorFrameworkDsl dslClient = client.with(watchMode, unhandledErrorListener, resultFilter, watcherFilter);
+ WatchableAsyncCuratorFramework watchableClient = isWatched ? dslClient.watched() : dslClient;
+
+ return new ModeledFrameworkImpl<>(
+ client,
+ dslClient,
+ watchableClient,
+ model,
+ watchMode,
+ watcherFilter,
+ unhandledErrorListener,
+ resultFilter,
+ isWatched
+ );
+ }
+
+ private ModeledFrameworkImpl(AsyncCuratorFramework client, AsyncCuratorFrameworkDsl dslClient, WatchableAsyncCuratorFramework watchableClient, ModelSpec<T> modelSpec, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter, boolean isWatched)
+ {
+ this.client = client;
+ this.dslClient = dslClient;
+ this.watchableClient = watchableClient;
+ this.modelSpec = modelSpec;
+ this.watchMode = watchMode;
+ this.watcherFilter = watcherFilter;
+ this.unhandledErrorListener = unhandledErrorListener;
+ this.resultFilter = resultFilter;
+ this.isWatched = isWatched;
+ }
+
+ @Override
+ public CachedModeledFramework<T> cached()
+ {
+ return cached(ThreadUtils.newSingleThreadExecutor("CachedModeledFramework"));
+ }
+
+ @Override
+ public CachedModeledFramework<T> cached(ExecutorService executor)
+ {
+ Preconditions.checkState(!isWatched, "CachedModeledFramework cannot be used with watched instances as the internal cache would bypass the watchers.");
+ return new CachedModeledFrameworkImpl<>(this, Objects.requireNonNull(executor, "executor cannot be null"));
+ }
+
+ @Override
+ public VersionedModeledFramework<T> versioned()
+ {
+ return new VersionedModeledFrameworkImpl<>(this);
+ }
+
+ @Override
+ public ModelSpec<T> modelSpec()
+ {
+ return modelSpec;
+ }
+
+ @Override
+ public AsyncCuratorFramework unwrap()
+ {
+ return client;
+ }
+
+ @Override
+ public AsyncStage<String> set(T item)
+ {
+ return set(item, null, -1);
+ }
+
+ @Override
+ public AsyncStage<String> set(T item, Stat storingStatIn)
+ {
+ return set(item, storingStatIn, -1);
+ }
+
+ @Override
+ public AsyncStage<String> set(T item, int version)
+ {
+ return set(item, null, -1);
+ }
+
+ @Override
+ public AsyncStage<String> set(T item, Stat storingStatIn, int version)
+ {
+ try
+ {
+ byte[] bytes = modelSpec.serializer().serialize(item);
+ return dslClient.create()
+ .withOptions(modelSpec.createOptions(), modelSpec.createMode(), fixAclList(modelSpec.aclList()), storingStatIn, modelSpec.ttl(), version)
+ .forPath(resolveForSet(item), bytes);
+ }
+ catch ( Exception e )
+ {
+ return ModelStage.exceptionally(e);
+ }
+ }
+
+ @Override
+ public AsyncStage<T> read()
+ {
+ return internalRead(ZNode::model, null);
+ }
+
+ @Override
+ public AsyncStage<T> read(Stat storingStatIn)
+ {
+ return internalRead(ZNode::model, storingStatIn);
+ }
+
+ @Override
+ public AsyncStage<ZNode<T>> readAsZNode()
+ {
+ return internalRead(Function.identity(), null);
+ }
+
+ @Override
+ public AsyncStage<Stat> update(T item)
+ {
+ return update(item, -1);
+ }
+
+ @Override
+ public AsyncStage<Stat> update(T item, int version)
+ {
+ try
+ {
+ byte[] bytes = modelSpec.serializer().serialize(item);
+ AsyncPathAndBytesable<AsyncStage<Stat>> next = isCompressed() ? dslClient.setData().compressedWithVersion(version) : dslClient.setData();
+ return next.forPath(resolveForSet(item), bytes);
+ }
+ catch ( Exception e )
+ {
+ return ModelStage.exceptionally(e);
+ }
+ }
+
+ @Override
+ public AsyncStage<Stat> checkExists()
+ {
+ return watchableClient.checkExists().forPath(modelSpec.path().fullPath());
+ }
+
+ @Override
+ public AsyncStage<Void> delete()
+ {
+ return delete(-1);
+ }
+
+ @Override
+ public AsyncStage<Void> delete(int version)
+ {
+ return dslClient.delete().withVersion(-1).forPath(modelSpec.path().fullPath());
+ }
+
+ @Override
+ public AsyncStage<List<ZPath>> children()
+ {
+ return internalGetChildren(modelSpec.path());
+ }
+
+ @Override
+ public AsyncStage<List<ZNode<T>>> childrenAsZNodes()
+ {
+ ModelStage<List<ZNode<T>>> modelStage = ModelStage.make();
+ Preconditions.checkState(!isWatched, "childrenAsZNodes() cannot be used with watched instances.");
+ children().handle((children, e) -> {
+ if ( e != null )
+ {
+ modelStage.completeExceptionally(e);
+ }
+ else
+ {
+ completeChildrenAsZNodes(modelStage, children);
+ }
+ return null;
+ });
+ return modelStage;
+ }
+
+ private void completeChildrenAsZNodes(ModelStage<List<ZNode<T>>> modelStage, List<ZPath> children)
+ {
+ List<ZNode<T>> nodes = Lists.newArrayList();
+ if ( children.size() == 0 )
+ {
+ modelStage.complete(nodes);
+ return;
+ }
+ children.forEach(path -> withPath(path).readAsZNode().handle((node, e) -> {
+ if ( e != null )
+ {
+ modelStage.completeExceptionally(e);
+ }
+ else
+ {
+ nodes.add(node);
+ if ( nodes.size() == children.size() )
+ {
+ modelStage.complete(nodes);
+ }
+ }
+ return null;
+ }));
+ }
+
+ private AsyncStage<List<ZPath>> internalGetChildren(ZPath path)
+ {
+ AsyncStage<List<String>> asyncStage = watchableClient.getChildren().forPath(path.fullPath());
+ ModelStage<List<ZPath>> modelStage = ModelStage.make(asyncStage.event());
+ asyncStage.whenComplete((children, e) -> {
+ if ( e != null )
+ {
+ modelStage.completeExceptionally(e);
+ }
+ else
+ {
+ modelStage.complete(children.stream().map(path::child).collect(Collectors.toList()));
+ }
+ });
+ return modelStage;
+ }
+
+ @Override
+ public ModeledFramework<T> parent()
+ {
+ ModelSpec<T> newModelSpec = modelSpec.parent();
+ return new ModeledFrameworkImpl<>(
+ client,
+ dslClient,
+ watchableClient,
+ newModelSpec,
+ watchMode,
+ watcherFilter,
+ unhandledErrorListener,
+ resultFilter,
+ isWatched
+ );
+ }
+
+ @Override
+ public ModeledFramework<T> child(Object child)
+ {
+ ModelSpec<T> newModelSpec = modelSpec.child(child);
+ return new ModeledFrameworkImpl<>(
+ client,
+ dslClient,
+ watchableClient,
+ newModelSpec,
+ watchMode,
+ watcherFilter,
+ unhandledErrorListener,
+ resultFilter,
+ isWatched
+ );
+ }
+
+ @Override
+ public ModeledFramework<T> withPath(ZPath path)
+ {
+ ModelSpec<T> newModelSpec = modelSpec.withPath(path);
+ return new ModeledFrameworkImpl<>(
+ client,
+ dslClient,
+ watchableClient,
+ newModelSpec,
+ watchMode,
+ watcherFilter,
+ unhandledErrorListener,
+ resultFilter,
+ isWatched
+ );
+ }
+
+ public static boolean isCompressed(Set<CreateOption> createOptions)
+ {
+ return createOptions.contains(CreateOption.compress);
+ }
+
+ @Override
+ public CuratorOp createOp(T model)
+ {
+ return client.transactionOp()
+ .create()
+ .withOptions(modelSpec.createMode(), fixAclList(modelSpec.aclList()), modelSpec.createOptions().contains(CreateOption.compress), modelSpec.ttl())
+ .forPath(resolveForSet(model), modelSpec.serializer().serialize(model));
+ }
+
+ @Override
+ public CuratorOp updateOp(T model)
+ {
+ return updateOp(model, -1);
+ }
+
+ @Override
+ public CuratorOp updateOp(T model, int version)
+ {
+ AsyncTransactionSetDataBuilder builder = client.transactionOp().setData();
+ if ( isCompressed() )
+ {
+ return builder.withVersionCompressed(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model));
+ }
+ return builder.withVersion(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model));
+ }
+
+ @Override
+ public CuratorOp deleteOp()
+ {
+ return deleteOp(-1);
+ }
+
+ @Override
+ public CuratorOp deleteOp(int version)
+ {
+ return client.transactionOp().delete().withVersion(version).forPath(modelSpec.path().fullPath());
+ }
+
+ @Override
+ public CuratorOp checkExistsOp()
+ {
+ return checkExistsOp(-1);
+ }
+
+ @Override
+ public CuratorOp checkExistsOp(int version)
+ {
+ return client.transactionOp().check().withVersion(version).forPath(modelSpec.path().fullPath());
+ }
+
+ @Override
+ public AsyncStage<List<CuratorTransactionResult>> inTransaction(List<CuratorOp> operations)
+ {
+ return client.transaction().forOperations(operations);
+ }
+
+ private boolean isCompressed()
+ {
+ return modelSpec.createOptions().contains(CreateOption.compress);
+ }
+
+ private <U> ModelStage<U> internalRead(Function<ZNode<T>, U> resolver, Stat storingStatIn)
+ {
+ Stat stat = (storingStatIn != null) ? storingStatIn : new Stat();
+ AsyncPathable<AsyncStage<byte[]>> next = isCompressed() ? watchableClient.getData().decompressedStoringStatIn(stat) : watchableClient.getData().storingStatIn(stat);
+ AsyncStage<byte[]> asyncStage = next.forPath(modelSpec.path().fullPath());
+ ModelStage<U> modelStage = ModelStage.make(asyncStage.event());
+ asyncStage.whenComplete((value, e) -> {
+ if ( e != null )
+ {
+ modelStage.completeExceptionally(e);
+ }
+ else
+ {
+ try
+ {
+ ZNode<T> node = new ZNodeImpl<>(modelSpec.path(), stat, modelSpec.serializer().deserialize(value));
+ modelStage.complete(resolver.apply(node));
+ }
+ catch ( Exception deserializeException )
+ {
+ modelStage.completeExceptionally(deserializeException);
+ }
+ }
+ });
+ return modelStage;
+ }
+
+ private String resolveForSet(T model)
+ {
+ if ( modelSpec.path().isResolved() )
+ {
+ return modelSpec.path().fullPath();
+ }
+ return modelSpec.path().resolved(model).fullPath();
+ }
+
+ private List<ACL> fixAclList(List<ACL> aclList)
+ {
+ return (aclList.size() > 0) ? aclList : null; // workaround for old, bad design. empty list not accepted
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/VersionedModeledFrameworkImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/VersionedModeledFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/VersionedModeledFrameworkImpl.java
new file mode 100644
index 0000000..89d7615
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/VersionedModeledFrameworkImpl.java
@@ -0,0 +1,85 @@
+/**
+ * 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.details;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.versioned.Versioned;
+import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework;
+import org.apache.zookeeper.data.Stat;
+
+class VersionedModeledFrameworkImpl<T> implements VersionedModeledFramework<T>
+{
+ private final ModeledFramework<T> client;
+
+ VersionedModeledFrameworkImpl(ModeledFramework<T> client)
+ {
+ this.client = client;
+ }
+
+ @Override
+ public AsyncStage<String> set(Versioned<T> model)
+ {
+ return client.set(model.model(), model.version());
+ }
+
+ @Override
+ public AsyncStage<String> set(Versioned<T> model, Stat storingStatIn)
+ {
+ return client.set(model.model(), storingStatIn, model.version());
+ }
+
+ @Override
+ public AsyncStage<Versioned<T>> read()
+ {
+ return read(null);
+ }
+
+ @Override
+ public AsyncStage<Versioned<T>> read(Stat storingStatIn)
+ {
+ Stat localStat = (storingStatIn != null) ? storingStatIn : new Stat();
+ AsyncStage<T> stage = client.read(localStat);
+ ModelStage<Versioned<T>> modelStage = ModelStage.make(stage.event());
+ stage.whenComplete((model, e) -> {
+ if ( e != null )
+ {
+ modelStage.completeExceptionally(e);
+ }
+ else
+ {
+ modelStage.complete(Versioned.from(model, localStat.getVersion()));
+ }
+ });
+ return modelStage;
+ }
+
+ @Override
+ public AsyncStage<Stat> update(Versioned<T> model)
+ {
+ return client.update(model.model(), model.version());
+ }
+
+ @Override
+ public CuratorOp updateOp(Versioned<T> model)
+ {
+ return client.updateOp(model.model(), model.version());
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZNodeImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZNodeImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZNodeImpl.java
new file mode 100644
index 0000000..85bedf4
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZNodeImpl.java
@@ -0,0 +1,56 @@
+/**
+ * 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.details;
+
+import org.apache.curator.x.async.modeled.ZNode;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.data.Stat;
+import java.util.Objects;
+
+public class ZNodeImpl<T> implements ZNode<T>
+{
+ private final ZPath path;
+ private final Stat stat;
+ private final T model;
+
+ public ZNodeImpl(ZPath path, Stat stat, T model)
+ {
+ this.path = Objects.requireNonNull(path, "path cannot be null");
+ this.stat = Objects.requireNonNull(stat, "stat cannot be null");
+ this.model = Objects.requireNonNull(model, "model cannot be null");
+ }
+
+ @Override
+ public ZPath path()
+ {
+ return path;
+ }
+
+ @Override
+ public Stat stat()
+ {
+ return stat;
+ }
+
+ @Override
+ public T model()
+ {
+ return model;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
new file mode 100644
index 0000000..fff742e
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
@@ -0,0 +1,289 @@
+/**
+ * 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.details;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import org.apache.curator.x.async.modeled.NodeName;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.common.PathUtils;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.function.UnaryOperator;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static org.apache.curator.utils.ZKPaths.PATH_SEPARATOR;
+
+public class ZPathImpl implements ZPath
+{
+ public static final ZPath root = new ZPathImpl(Collections.singletonList(PATH_SEPARATOR), null);
+
+ private final List<String> nodes;
+ private final boolean isResolved;
+ private volatile String fullPath = null;
+ private volatile ZPath parent = null;
+ private volatile Pattern schema = null;
+
+ public static ZPath parse(String fullPath, UnaryOperator<String> nameFilter)
+ {
+ return parseInternal(fullPath, nameFilter);
+ }
+
+ private static ZPathImpl parseInternal(String fullPath, UnaryOperator<String> nameFilter)
+ {
+ List<String> nodes = ImmutableList.<String>builder()
+ .add(PATH_SEPARATOR)
+ .addAll(
+ Splitter.on(PATH_SEPARATOR)
+ .omitEmptyStrings()
+ .splitToList(fullPath)
+ .stream()
+ .map(nameFilter)
+ .collect(Collectors.toList())
+ )
+ .build();
+ nodes.forEach(ZPathImpl::validate);
+ return new ZPathImpl(nodes, null);
+ }
+
+ public static ZPath from(String[] names)
+ {
+ return from(null, Arrays.asList(names));
+ }
+
+ public static ZPath from(List<String> names)
+ {
+ return from(null, names);
+ }
+
+ public static ZPath from(ZPath base, String[] names)
+ {
+ return from(base, Arrays.asList(names));
+ }
+
+ public static ZPath from(ZPath base, List<String> names)
+ {
+ names = Objects.requireNonNull(names, "names cannot be null");
+ names.forEach(ZPathImpl::validate);
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ if ( base != null )
+ {
+ if ( base instanceof ZPathImpl )
+ {
+ builder.addAll(((ZPathImpl)base).nodes);
+ }
+ else
+ {
+ builder.addAll(Splitter.on(PATH_SEPARATOR).omitEmptyStrings().splitToList(base.fullPath()));
+ }
+ }
+ else
+ {
+ builder.add(PATH_SEPARATOR);
+ }
+ List<String> nodes = builder.addAll(names).build();
+ return new ZPathImpl(nodes, null);
+ }
+
+ @Override
+ public ZPath child(Object child)
+ {
+ return new ZPathImpl(nodes, NodeName.nameFrom(child));
+ }
+
+ @Override
+ public ZPath parent()
+ {
+ checkRootAccess();
+ if ( parent == null )
+ {
+ parent = new ZPathImpl(nodes.subList(0, nodes.size() - 1), null);
+ }
+ return parent;
+ }
+
+ @Override
+ public boolean isRoot()
+ {
+ return nodes.size() == 1;
+ }
+
+ @Override
+ public boolean startsWith(ZPath path)
+ {
+ ZPathImpl rhs;
+ if ( path instanceof ZPathImpl )
+ {
+ rhs = (ZPathImpl)path;
+ }
+ else
+ {
+ rhs = parseInternal(path.fullPath(), s -> s);
+ }
+ return (nodes.size() >= rhs.nodes.size()) && nodes.subList(0, rhs.nodes.size()).equals(rhs.nodes);
+ }
+
+ @Override
+ public Pattern toSchemaPathPattern()
+ {
+ if ( schema == null )
+ {
+ schema = Pattern.compile(buildFullPath(s -> isParameter(s) ? ".*" : s));
+ }
+ return schema;
+ }
+
+ @Override
+ public String fullPath()
+ {
+ checkResolved();
+ if ( fullPath == null )
+ {
+ fullPath = buildFullPath(s -> s);
+ }
+ return fullPath;
+ }
+
+ @Override
+ public String nodeName()
+ {
+ return nodes.get(nodes.size() - 1);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ ZPathImpl zPaths = (ZPathImpl)o;
+
+ return nodes.equals(zPaths.nodes);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return nodes.hashCode();
+ }
+
+ @Override
+ public String toString()
+ {
+ return nodes.subList(1, nodes.size())
+ .stream().map(name -> isParameter(name) ? name.substring(1) : name)
+ .collect(Collectors.joining(PATH_SEPARATOR, PATH_SEPARATOR, ""));
+ }
+
+ @Override
+ public ZPath resolved(List<Object> parameters)
+ {
+ Iterator<Object> iterator = parameters.iterator();
+ List<String> nodeNames = nodes.stream()
+ .map(name -> {
+ if ( isParameter(name) && iterator.hasNext() )
+ {
+ return NodeName.nameFrom(iterator.next());
+ }
+ return name;
+ })
+ .collect(Collectors.toList());
+ return new ZPathImpl(nodeNames, null);
+ }
+
+ @Override
+ public boolean isResolved()
+ {
+ return isResolved;
+ }
+
+ private static boolean isParameter(String name)
+ {
+ return (name.length() > 1) && name.startsWith(PATH_SEPARATOR);
+ }
+
+ private ZPathImpl(List<String> nodes, String child)
+ {
+ ImmutableList.Builder<String> builder = ImmutableList.<String>builder().addAll(nodes);
+ if ( child != null )
+ {
+ validate(child);
+ builder.add(child);
+ }
+ this.nodes = builder.build();
+ isResolved = this.nodes.stream().noneMatch(ZPathImpl::isParameter);
+ }
+
+ private void checkRootAccess()
+ {
+ if ( isRoot() )
+ {
+ throw new NoSuchElementException("The root has no parent");
+ }
+ }
+
+ private void checkResolved()
+ {
+ if ( !isResolved)
+ {
+ throw new IllegalStateException("This ZPath has not been resolved: " + toString());
+ }
+ }
+
+ private static void validate(String nodeName)
+ {
+ if ( isParameter(Objects.requireNonNull(nodeName, "nodeName cannot be null")) )
+ {
+ return;
+ }
+ if ( nodeName.equals(PATH_SEPARATOR) )
+ {
+ return;
+ }
+ PathUtils.validatePath(PATH_SEPARATOR + nodeName);
+ }
+
+ private String buildFullPath(UnaryOperator<String> filter)
+ {
+ boolean addSeparator = false;
+ StringBuilder str = new StringBuilder();
+ int size = nodes.size();
+ int parameterIndex = 0;
+ for ( int i = 0; i < size; ++i )
+ {
+ if ( i > 1 )
+ {
+ str.append(PATH_SEPARATOR);
+ }
+ str.append(filter.apply(nodes.get(i)));
+ }
+ return str.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec.java
new file mode 100644
index 0000000..3fa9831
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec.java
@@ -0,0 +1,87 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * <p>
+ * Abstraction that allows the construction of ModelSpecs using strongly typed parameter replacements.
+ * For example, given a ModelSpec with a path such as "/root/registry/people/{id}" where "id" should
+ * be <code>PersonId</code>.
+ * </p>
+ *
+ * <p>
+ * <pre><code>
+ * // Step 1. Create a typed ZPath
+ * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
+ *
+ * // Step 2. Create a ModelSpec builder (do not build at this point)
+ * ModelSpecBuilder<Person> builder = ModelSpec.builder(JacksonModelSerializer.build(Person.class))
+ *
+ * // Step 3. Create a typed ModelSpec using the typed ZPath and ModelSpec builder
+ * TypedModelSpec<Person, PersonId> typedModelSpec = TypedModelSpec.from(builder, path);
+ *
+ * // later on the TypedModelSpec can be resolved into a useable ModelSpec
+ * ModelSpec<Person> modelSpec = typedModelSpec.resolve(personId);
+ * </pre></code>
+ * </p>
+ */
+@FunctionalInterface
+public interface TypedModelSpec<M, P1>
+{
+ /**
+ * Resolve into a ZPath using the given parameter
+ *
+ * @param p1 the parameter
+ * @return ZPath
+ */
+ ModelSpec<M> resolved(P1 p1);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1> TypedModelSpec<M, P1> from(ModelSpecBuilder<M> builder, TypedZPath<P1> path)
+ {
+ return p1 -> builder.withPath(path.resolved(p1)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1> TypedModelSpec<M, P1> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath<P1> zPath = TypedZPath.from(pathWithIds);
+ return p1 -> builder.withPath(zPath.resolved(p1)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec0.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec0.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec0.java
new file mode 100644
index 0000000..dee3506
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec0.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link TypedModelSpec}, but with 0 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec0<M>
+{
+ ModelSpec<M> resolved();
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved()} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M> TypedModelSpec0<M> from(ModelSpecBuilder<M> builder, TypedZPath0 path)
+ {
+ return () -> builder.withPath(path.resolved()).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved()} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M> TypedModelSpec0<M> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath0 zPath = TypedZPath0.from(pathWithIds);
+ return () -> builder.withPath(zPath.resolved()).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec10.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec10.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec10.java
new file mode 100644
index 0000000..1b00d66
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec10.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 10 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> TypedModelSpec10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> from(ModelSpecBuilder<M> builder, TypedZPath10<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> TypedModelSpec10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath10<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> zPath = TypedZPath10.from(pathWithIds);
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec2.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec2.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec2.java
new file mode 100644
index 0000000..a56e139
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec2.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 2 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec2<M, P1, P2>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2> TypedModelSpec2<M, P1, P2> from(ModelSpecBuilder<M> builder, TypedZPath2<P1, P2> path)
+ {
+ return (p1, p2) -> builder.withPath(path.resolved(p1, p2)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2> TypedModelSpec2<M, P1, P2> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath2<P1, P2> zPath = TypedZPath2.from(pathWithIds);
+ return (p1, p2) -> builder.withPath(zPath.resolved(p1, p2)).build();
+ }
+}
[3/6] curator git commit: Squashed commit of the following:
Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec3.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec3.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec3.java
new file mode 100644
index 0000000..f4e808c
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec3.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 3 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec3<M, P1, P2, P3>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3> TypedModelSpec3<M, P1, P2, P3> from(ModelSpecBuilder<M> builder, TypedZPath3<P1, P2, P3> path)
+ {
+ return (p1, p2, p3) -> builder.withPath(path.resolved(p1, p2, p3)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3> TypedModelSpec3<M, P1, P2, P3> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath3<P1, P2, P3> zPath = TypedZPath3.from(pathWithIds);
+ return (p1, p2, p3) -> builder.withPath(zPath.resolved(p1, p2, p3)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec4.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec4.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec4.java
new file mode 100644
index 0000000..040f8a6
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec4.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 4 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec4<M, P1, P2, P3, P4>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4> TypedModelSpec4<M, P1, P2, P3, P4> from(ModelSpecBuilder<M> builder, TypedZPath4<P1, P2, P3, P4> path)
+ {
+ return (p1, p2, p3, p4) -> builder.withPath(path.resolved(p1, p2, p3, p4)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4> TypedModelSpec4<M, P1, P2, P3, P4> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath4<P1, P2, P3, P4> zPath = TypedZPath4.from(pathWithIds);
+ return (p1, p2, p3, p4) -> builder.withPath(zPath.resolved(p1, p2, p3, p4)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec5.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec5.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec5.java
new file mode 100644
index 0000000..b67f5b0
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec5.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 5 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec5<M, P1, P2, P3, P4, P5>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5> TypedModelSpec5<M, P1, P2, P3, P4, P5> from(ModelSpecBuilder<M> builder, TypedZPath5<P1, P2, P3, P4, P5> path)
+ {
+ return (p1, p2, p3, p4, p5) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5> TypedModelSpec5<M, P1, P2, P3, P4, P5> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath5<P1, P2, P3, P4, P5> zPath = TypedZPath5.from(pathWithIds);
+ return (p1, p2, p3, p4, p5) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec6.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec6.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec6.java
new file mode 100644
index 0000000..675029a
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec6.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 6 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec6<M, P1, P2, P3, P4, P5, P6>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6> TypedModelSpec6<M, P1, P2, P3, P4, P5, P6> from(ModelSpecBuilder<M> builder, TypedZPath6<P1, P2, P3, P4, P5, P6> path)
+ {
+ return (p1, p2, p3, p4, p5, p6) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6> TypedModelSpec6<M, P1, P2, P3, P4, P5, P6> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath6<P1, P2, P3, P4, P5, P6> zPath = TypedZPath6.from(pathWithIds);
+ return (p1, p2, p3, p4, p5, p6) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec7.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec7.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec7.java
new file mode 100644
index 0000000..8ffd3c0
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec7.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 7 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec7<M, P1, P2, P3, P4, P5, P6, P7>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7> TypedModelSpec7<M, P1, P2, P3, P4, P5, P6, P7> from(ModelSpecBuilder<M> builder, TypedZPath7<P1, P2, P3, P4, P5, P6, P7> path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7> TypedModelSpec7<M, P1, P2, P3, P4, P5, P6, P7> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath7<P1, P2, P3, P4, P5, P6, P7> zPath = TypedZPath7.from(pathWithIds);
+ return (p1, p2, p3, p4, p5, p6, p7) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec8.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec8.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec8.java
new file mode 100644
index 0000000..3a9acd4
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec8.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 8 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec8<M, P1, P2, P3, P4, P5, P6, P7, P8>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8> TypedModelSpec8<M, P1, P2, P3, P4, P5, P6, P7, P8> from(ModelSpecBuilder<M> builder, TypedZPath8<P1, P2, P3, P4, P5, P6, P7, P8> path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8> TypedModelSpec8<M, P1, P2, P3, P4, P5, P6, P7, P8> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath8<P1, P2, P3, P4, P5, P6, P7, P8> zPath = TypedZPath8.from(pathWithIds);
+ return (p1, p2, p3, p4, p5, p6, p7, p8) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec9.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec9.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec9.java
new file mode 100644
index 0000000..94ff8d7
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec9.java
@@ -0,0 +1,61 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 9 parameters
+ */
+@FunctionalInterface
+public interface TypedModelSpec9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9>
+{
+ ModelSpec<M> resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9);
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and typed path. When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param path typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9> TypedModelSpec9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> from(ModelSpecBuilder<M> builder, TypedZPath9<P1, P2, P3, P4, P5, P6, P7, P8, P9> path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build();
+ }
+
+ /**
+ * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath
+ * is created from the given full path and When
+ * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the
+ * resolved path
+ *
+ * @param builder model spec builder
+ * @param pathWithIds typed path
+ * @return new TypedModelSpec
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9> TypedModelSpec9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> from(ModelSpecBuilder<M> builder, String pathWithIds)
+ {
+ TypedZPath9<P1, P2, P3, P4, P5, P6, P7, P8, P9> zPath = TypedZPath9.from(pathWithIds);
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework.java
new file mode 100644
index 0000000..56a8ebc
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework.java
@@ -0,0 +1,93 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * <p>
+ * Abstraction that allows the construction of ModeledFrameworks using strongly typed parameter replacements.
+ * For example, given a ModeledFramework with a ModelSpec that has a path such as
+ * "/root/registry/people/{id}" where "id" should be <code>PersonId</code>.
+ * </p>
+ *
+ * <p>
+ * <pre><code>
+ * // Step 1. Create a typed ZPath
+ * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
+ *
+ * // Step 2. Create a typed ModelSpec (see TypedModelSpec for details)
+ * TypedModelSpec<Person, PersonId> typedModelSpec = TypedModelSpec.from(builder, path);
+ *
+ * // Step 3. Create a ModeledFramework builder (do not build at this point)
+ * ModeledFrameworkBuilder<Person> builder = ModeledFramework.builder()... // add any other needed options
+ *
+ * // Step 4. Create a typed TypedModeledFramework using the typed ZPath, typed ModelSpec, and ModeledFramework builder
+ * TypedModeledFramework<Person, PersonId> clientSpec = TypedModeledFramework.from(builder, modelSpec);
+ *
+ * // later on the TypedModelSpec can be resolved into a useable ModeledFramework
+ * ModeledFramework<Person> client = clientSpec.resolve(personId);
+ * </pre></code>
+ * </p>
+ */
+@FunctionalInterface
+public interface TypedModeledFramework<M, P1>
+{
+ /**
+ * Resolve into a ModeledFramework using the given parameter
+ *
+ * @param client the curator instance to use
+ * @param p1 the parameter
+ * @return ZPath
+ */
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1> TypedModeledFramework<M, P1> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec<M, P1> modelSpec)
+ {
+ return (client, p1) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1> TypedModeledFramework<M, P1> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec<M, P1> typedModelSpec = TypedModelSpec.from(modelSpecBuilder, pathWithIds);
+ return (client, p1) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework0.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework0.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework0.java
new file mode 100644
index 0000000..8c9de04
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework0.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link TypedModeledFramework}, but with 0 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework0<M>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(org.apache.curator.x.async.AsyncCuratorFramework)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M> TypedModeledFramework0<M> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec0<M> modelSpec)
+ {
+ return (client) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved()).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(org.apache.curator.x.async.AsyncCuratorFramework)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M> TypedModeledFramework0<M> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec0<M> typedModelSpec = TypedModelSpec0.from(modelSpecBuilder, pathWithIds);
+ return (client) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved()).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework10.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework10.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework10.java
new file mode 100644
index 0000000..24a4671
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework10.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 10 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> TypedModeledFramework10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> TypedModeledFramework10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec10<M, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> typedModelSpec = TypedModelSpec10.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework2.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework2.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework2.java
new file mode 100644
index 0000000..9a535e0
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework2.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 2 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework2<M, P1, P2>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2> TypedModeledFramework2<M, P1, P2> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec2<M, P1, P2> modelSpec)
+ {
+ return (client, p1, p2) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2> TypedModeledFramework2<M, P1, P2> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec2<M, P1, P2> typedModelSpec = TypedModelSpec2.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework3.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework3.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework3.java
new file mode 100644
index 0000000..6d6edc3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework3.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 3 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework3<M, P1, P2, P3>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3> TypedModeledFramework3<M, P1, P2, P3> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec3<M, P1, P2, P3> modelSpec)
+ {
+ return (client, p1, p2, p3) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3> TypedModeledFramework3<M, P1, P2, P3> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec3<M, P1, P2, P3> typedModelSpec = TypedModelSpec3.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework4.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework4.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework4.java
new file mode 100644
index 0000000..ff88a0f
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework4.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 4 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework4<M, P1, P2, P3, P4>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4> TypedModeledFramework4<M, P1, P2, P3, P4> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec4<M, P1, P2, P3, P4> modelSpec)
+ {
+ return (client, p1, p2, p3, p4) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4> TypedModeledFramework4<M, P1, P2, P3, P4> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec4<M, P1, P2, P3, P4> typedModelSpec = TypedModelSpec4.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework5.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework5.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework5.java
new file mode 100644
index 0000000..7579158
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework5.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 5 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework5<M, P1, P2, P3, P4, P5>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5> TypedModeledFramework5<M, P1, P2, P3, P4, P5> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec5<M, P1, P2, P3, P4, P5> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5> TypedModeledFramework5<M, P1, P2, P3, P4, P5> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec5<M, P1, P2, P3, P4, P5> typedModelSpec = TypedModelSpec5.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework6.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework6.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework6.java
new file mode 100644
index 0000000..9b30a52
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework6.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 6 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework6<M, P1, P2, P3, P4, P5, P6>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6> TypedModeledFramework6<M, P1, P2, P3, P4, P5, P6> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec6<M, P1, P2, P3, P4, P5, P6> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5, p6) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6> TypedModeledFramework6<M, P1, P2, P3, P4, P5, P6> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec6<M, P1, P2, P3, P4, P5, P6> typedModelSpec = TypedModelSpec6.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5, p6) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework7.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework7.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework7.java
new file mode 100644
index 0000000..f4947f2
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework7.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 7 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework7<M, P1, P2, P3, P4, P5, P6, P7>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7> TypedModeledFramework7<M, P1, P2, P3, P4, P5, P6, P7> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec7<M, P1, P2, P3, P4, P5, P6, P7> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5, p6, p7) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7> TypedModeledFramework7<M, P1, P2, P3, P4, P5, P6, P7> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec7<M, P1, P2, P3, P4, P5, P6, P7> typedModelSpec = TypedModelSpec7.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5, p6, p7) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework8.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework8.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework8.java
new file mode 100644
index 0000000..2864df1
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework8.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 8 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework8<M, P1, P2, P3, P4, P5, P6, P7, P8>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8> TypedModeledFramework8<M, P1, P2, P3, P4, P5, P6, P7, P8> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec8<M, P1, P2, P3, P4, P5, P6, P7, P8> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8> TypedModeledFramework8<M, P1, P2, P3, P4, P5, P6, P7, P8> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec8<M, P1, P2, P3, P4, P5, P6, P7, P8> typedModelSpec = TypedModelSpec8.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework9.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework9.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework9.java
new file mode 100644
index 0000000..6102943
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModeledFramework9.java
@@ -0,0 +1,63 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 9 parameters
+ */
+@FunctionalInterface
+public interface TypedModeledFramework9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9>
+{
+ ModeledFramework<M> resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9);
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpec TypedModelSpec
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9> TypedModeledFramework9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> from(ModeledFrameworkBuilder<M> frameworkBuilder, TypedModelSpec9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> modelSpec)
+ {
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build();
+ }
+
+ /**
+ * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids.
+ * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the
+ * resolved model spec and resolved path
+ *
+ * @param frameworkBuilder ModeledFrameworkBuilder
+ * @param modelSpecBuilder model spec builder
+ * @param pathWithIds path with {XXXX} parameters
+ * @return new TypedModeledFramework
+ */
+ static <M, P1, P2, P3, P4, P5, P6, P7, P8, P9> TypedModeledFramework9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> from(ModeledFrameworkBuilder<M> frameworkBuilder, ModelSpecBuilder<M> modelSpecBuilder, String pathWithIds)
+ {
+ TypedModelSpec9<M, P1, P2, P3, P4, P5, P6, P7, P8, P9> typedModelSpec = TypedModelSpec9.from(modelSpecBuilder, pathWithIds);
+ return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath.java
new file mode 100644
index 0000000..ed3dcd3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath.java
@@ -0,0 +1,92 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * <p>
+ * Abstraction that allows the construction of ZPaths using strongly typed parameter replacements.
+ * For example, given a path such as "/root/registry/people/{id}" where "id" should be <code>PersonId</code>.
+ * </p>
+ *
+ * <p>
+ * <pre><code>
+ * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
+ *
+ * ...
+ *
+ * ZPath path = typedPath.resolved(personId);
+ * </pre></code>
+ * </p>
+ *
+ * <p>
+ * Additionally, if you have a model/class that implements {@link org.apache.curator.x.async.modeled.NodeName}
+ * you can pass that when resolving. E.g.
+ * </p>
+ *
+ * <p>
+ * <pre><code>
+ * public class MyModel implements NodeName {
+ * ...
+ * public String nodeName() {
+ * return modelId;
+ * }
+ * }
+ *
+ * TypedZPath<MyModel> typedPath = TypedZPath.from("/foo/bar/{id}");
+ *
+ * MyModel model = ...
+ * ZPath path = typedPath.resolved(model);
+ * </pre></code>
+ * </p>
+ */
+@FunctionalInterface
+public interface TypedZPath<T>
+{
+ /**
+ * Resolve into a ZPath using the given parameter
+ *
+ * @param p1 the parameter
+ * @return ZPath
+ */
+ ZPath resolved(T p1);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T> TypedZPath<T> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T> TypedZPath<T> from(ZPath path)
+ {
+ return path::resolved;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath0.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath0.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath0.java
new file mode 100644
index 0000000..60576b2
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath0.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link TypedZPath}, but with 0 parameters.
+ */
+@FunctionalInterface
+public interface TypedZPath0
+{
+ ZPath resolved();
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static TypedZPath0 from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static TypedZPath0 from(ZPath path)
+ {
+ return path::resolved;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath10.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath10.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath10.java
new file mode 100644
index 0000000..b29f5ef
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath10.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 10 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9, T10 p10);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> TypedZPath10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> TypedZPath10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath2.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath2.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath2.java
new file mode 100644
index 0000000..1feed76
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath2.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 2 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath2<T1, T2>
+{
+ ZPath resolved(T1 p1, T2 p2);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2> TypedZPath2<T1, T2> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2> TypedZPath2<T1, T2> from(ZPath path)
+ {
+ return (p1, p2) -> path.resolved(p1, p2);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath3.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath3.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath3.java
new file mode 100644
index 0000000..91185ce
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath3.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 3 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath3<T1, T2, T3>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3> TypedZPath3<T1, T2, T3> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3> TypedZPath3<T1, T2, T3> from(ZPath path)
+ {
+ return (p1, p2, p3) -> path.resolved(p1, p2, p3);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath4.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath4.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath4.java
new file mode 100644
index 0000000..be4905b
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath4.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 4 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath4<T1, T2, T3, T4>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4> TypedZPath4<T1, T2, T3, T4> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4> TypedZPath4<T1, T2, T3, T4> from(ZPath path)
+ {
+ return (p1, p2, p3, p4) -> path.resolved(p1, p2, p3, p4);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath5.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath5.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath5.java
new file mode 100644
index 0000000..8c7311d
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath5.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 5 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath5<T1, T2, T3, T4, T5>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5> TypedZPath5<T1, T2, T3, T4, T5> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5> TypedZPath5<T1, T2, T3, T4, T5> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5) -> path.resolved(p1, p2, p3, p4, p5);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath6.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath6.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath6.java
new file mode 100644
index 0000000..6e05be9
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath6.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 6 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath6<T1, T2, T3, T4, T5, T6>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6> TypedZPath6<T1, T2, T3, T4, T5, T6> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6> TypedZPath6<T1, T2, T3, T4, T5, T6> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5, p6) -> path.resolved(p1, p2, p3, p4, p5, p6);
+ }
+}
[6/6] curator git commit: Squashed commit of the following:
Posted by ra...@apache.org.
Squashed commit of the following:
commit 588dca66e6b60161bd66488edd2df211a8e2c3a6
Merge: 7e6551f3 8b28b120
Author: randgalt <ra...@apache.org>
Date: Fri Jul 7 22:28:15 2017 -0500
Merge branch 'master' into CURATOR-397
commit 7e6551f3fe3f1abbcf797739df20acc5a4e5be16
Author: randgalt <ra...@apache.org>
Date: Thu Jun 29 23:21:02 2017 -0500
completeChildrenAsZNodes() wasn't handling empty children listsw
commit 738c3617ee4887fefcf8dd5082b9b71c9f7021db
Author: randgalt <ra...@apache.org>
Date: Thu Jun 29 23:20:45 2017 -0500
no need to use EnsureContainers object
commit 09f9bc06accf7c42d3a5dcf3d92c20d706e56ea6
Author: randgalt <ra...@apache.org>
Date: Wed Jun 28 09:46:32 2017 -0500
Added asyncEnsureContainers
commit f7d410f8e11a840a36c7bb97313ed3fb7edf73ca
Author: randgalt <ra...@apache.org>
Date: Wed Jun 28 09:19:03 2017 -0500
It doesn't make sense for cached() to cache the root node. So, don't
commit b83bef9de0c980dfe21c85d0c08abb1bd75a06f5
Author: randgalt <ra...@apache.org>
Date: Wed Jun 28 00:07:29 2017 -0500
oops - orSetData() was returning null
commit 318bed14f050dd579f76a768bcc7c3125d832049
Author: randgalt <ra...@apache.org>
Date: Tue Jun 27 00:10:24 2017 -0500
version of AsyncLocker.release() that ignores unheld locks
commit eeabe8b5136e8a46569ede9ea193f6e2f10153b1
Author: randgalt <ra...@apache.org>
Date: Wed Jun 14 16:10:35 2017 -0500
completeChildrenAsZNodes was treating zPaths as node names
commit f0bcd0476e30a876f7995d191d8811fadb1a201e
Author: randgalt <ra...@apache.org>
Date: Tue Jun 13 07:34:25 2017 -0500
Added lockAsyncIf to AsyncLock
commit fbf25adcf88b4198fdecd18a494b615c70bf763c
Author: randgalt <ra...@apache.org>
Date: Sat Jun 10 22:22:08 2017 -0500
Made the API of AsyncLocker cleaner
commit e943763f0d299b5e69e6a7f4e871024b7aa95503
Author: randgalt <ra...@apache.org>
Date: Sat Jun 10 20:46:19 2017 -0500
Added AsyncLocker
commit 93f11ed09e0cc6e9c6b80b1a88796d027f4f3d7a
Author: randgalt <ra...@apache.org>
Date: Sat Jun 10 19:52:15 2017 -0500
1. remove siblings added childrenAsZNodes
2. Add util to unwrap stages of ZNodes
commit 55df07e6ab5417943d485e88f7efa89df0b6e52d
Author: randgalt <ra...@apache.org>
Date: Fri Jun 9 23:55:20 2017 -0500
Added list()
commit f32a5fb4eeb63251ad769f54f7fee255c2bee61e
Author: randgalt <ra...@apache.org>
Date: Fri Jun 9 23:54:51 2017 -0500
Added '0' versions of Typed classes as a convenience so that the same idiom can be used even if there are no real type arguments
commit 523f0c8095fcde7033b9caa4c5423301085b0e16
Author: randgalt <ra...@apache.org>
Date: Fri Jun 9 18:01:58 2017 -0500
Added readThrough methods
commit 6428a9238892eba6e9f89551ed4602d2a7c5a04f
Author: randgalt <ra...@apache.org>
Date: Thu May 18 15:32:38 2017 +0200
Added a test for ACLs
commit 7a15af6ac5a58f1aba5dd6569d11b3a46fc45f9d
Author: randgalt <ra...@apache.org>
Date: Thu May 11 17:10:43 2017 +0200
CachedModeledFramework now handles unresolved paths where the final node is a parameter.
commit bb36c48ef420d48e345368d03e4696f22d394126
Author: randgalt <ra...@apache.org>
Date: Thu May 11 16:53:30 2017 +0200
updated doc for new auto-resolve behavior
commit 3338c05c997068c93fe4f889f4d0c1ee3fc3a776
Author: randgalt <ra...@apache.org>
Date: Thu May 11 16:03:53 2017 +0200
renamed at() to child()
commit 05cab2ec57208d3aa87202ff2d4f44146d5ba6b5
Author: randgalt <ra...@apache.org>
Date: Thu May 11 16:01:59 2017 +0200
Updated sub/pub test to use new resolving features so that at() isn't necessary
commit 6ea7221ab48dd0597e3b0f2ba4cdc33147ff7d4d
Author: randgalt <ra...@apache.org>
Date: Thu May 11 15:55:31 2017 +0200
1. Support partially resolved paths
2. Added method to get siblings
3. Auto resolve unresolved paths on set/update using the model being set/updated
commit 7ed263cc37d1c6eacfe676f7c78096d7086b9c59
Author: randgalt <ra...@apache.org>
Date: Thu May 11 14:04:57 2017 +0200
doc fixes
commit ed00d50f81cb224f9e2690aaa425adae6b0da0bd
Author: randgalt <ra...@apache.org>
Date: Thu May 11 13:52:51 2017 +0200
doc fixes
commit 6037c7b270b12dce273aa67939a78a4c00b5155f
Author: randgalt <ra...@apache.org>
Date: Thu May 11 12:53:49 2017 +0200
further refinement of previous change. Not all completion handlers should be in the cache's thread. But, allow for the default async to use it
commit 396d98a51495ec1c60156dcd0d6d553644e43689
Author: randgalt <ra...@apache.org>
Date: Wed May 10 23:38:07 2017 +0200
More tests, refactoring
commit 6485f1650e37e392e6122f8b13ff432ee0321666
Author: randgalt <ra...@apache.org>
Date: Wed May 10 19:23:00 2017 +0200
CachedModeledFrameworkImpl wasn't using executor in right way. Completions need to happen async not setting of the value. This required creating CachedStage which proxies all non-async methods to their async counterparts using the CachedModeledFrameworkImpl's executor
commit 80ca587e5e543568f06780ad8a8f874b94333218
Author: randgalt <ra...@apache.org>
Date: Wed May 10 16:09:55 2017 +0200
more tests
commit 49743a487e3c159029906bb334989e6f1c82f6a8
Author: randgalt <ra...@apache.org>
Date: Wed May 10 15:00:05 2017 +0200
Allow the {id} in ZPaths to use any value. This is very useful for making paths easier to read and debug. So, you can now do: '/root/{org}/employee/{emp id}'
commit cba43b342400461e0d4f0dfd894b69a8b1891438
Author: randgalt <ra...@apache.org>
Date: Wed May 10 14:09:12 2017 +0200
doc updates
commit 13e17cbbfe089554f47d64449eea1302b9b6bd19
Author: randgalt <ra...@apache.org>
Date: Wed May 10 12:43:59 2017 +0200
minor update
commit a7d2e058e4fedb00c408694f185b54ec4259bf94
Author: randgalt <ra...@apache.org>
Date: Wed May 10 11:58:04 2017 +0200
final versioned APIs and doc
commit beac06f136f26dd3dabb754dcb999876d614244b
Author: randgalt <ra...@apache.org>
Date: Tue May 9 23:16:54 2017 +0200
Added Versioned facade for easy management of models with versions
commit 23a1487984b576b878e3a88287f74d9381e5dec7
Author: randgalt <ra...@apache.org>
Date: Mon May 8 19:11:22 2017 +0200
Work on testing modeled schema
commit 555e1d4684e233e1caa910be67a4ff892253d9c7
Merge: e4a7e091 32a7755b
Author: randgalt <ra...@apache.org>
Date: Mon May 8 18:36:48 2017 +0200
Merge branch 'master' into CURATOR-397
commit e4a7e09172c7d9d14081233dec88690ecba6df9e
Author: randgalt <ra...@apache.org>
Date: Mon May 8 05:42:17 2017 +0200
some refactoring, refinement
commit c002e22e5a613cd8426c2e8b407ab06957908526
Author: randgalt <ra...@apache.org>
Date: Sun May 7 10:26:48 2017 +0200
removed ZPath 'resolving'. It doesn't add much value and muddies up the code
commit e95b885ebf0240e61d8de1c2644f8fb11cc2eca5
Author: randgalt <ra...@apache.org>
Date: Sun May 7 09:58:07 2017 +0200
Allow setting a version for orSetData()
commit 1110ab3bbc55748c053adcd909cc0d82be84309d
Author: randgalt <ra...@apache.org>
Date: Sun May 7 09:55:28 2017 +0200
Support setting the version when doing a create or set operation
commit c18783924af4b4c3f11d098165c85bf313f454c2
Author: randgalt <ra...@apache.org>
Date: Sun May 7 09:41:55 2017 +0200
Added variant to readAsZNode
commit fe0a88b3b7b9fccf9da022bc0a13e440b1f8435c
Author: randgalt <ra...@apache.org>
Date: Fri May 5 18:38:46 2017 -0400
better exception handling for serialization issues
commit f2370b7710b85850936f48404c6959d45ed03626
Author: randgalt <ra...@apache.org>
Date: Thu May 4 23:15:14 2017 -0500
1. Allow for an Executor service to be passed to the cache
2. CachedModeledFramework must complete the stages via a thread as the caller is expected async processing
commit 29b09ceb5d305419428760e07d572dd4b920cb74
Author: randgalt <ra...@apache.org>
Date: Thu May 4 15:35:05 2017 -0500
updated doc
commit 0bc3a9bd2e63bee511b0d78f584f862e875c8365
Author: randgalt <ra...@apache.org>
Date: Thu May 4 10:30:13 2017 -0500
some refactoring and doc
commit 1793675c136bf0ba89a33f9c5490e9ab5c7f0000
Author: randgalt <ra...@apache.org>
Date: Thu May 4 10:16:55 2017 -0500
Allow type model specs and typed clients to be created in "one shot". This made writing the sub-pub example simpler and easier to understand.
commit 5ac1a3314e372d8bd22ef7bba704b5621a1638b8
Author: randgalt <ra...@apache.org>
Date: Wed May 3 22:59:47 2017 -0500
finished docs for the pub-sub example
commit c97fae4ef6a06301f2a794df6e23411b303f8cf1
Merge: 71e28e1a f898959e
Author: randgalt <ra...@apache.org>
Date: Wed May 3 22:18:38 2017 -0500
Merge branch 'master' into CURATOR-397
commit 71e28e1a6b97171b3c54da0a4dc82564247e7f24
Author: randgalt <ra...@apache.org>
Date: Wed May 3 21:50:59 2017 -0500
starting read me for the sub-pub example
commit aa86931b9392b0d6f0cf6ff161afd82e9e3749f1
Author: randgalt <ra...@apache.org>
Date: Wed May 3 17:02:31 2017 -0500
added lots of doc
commit ea47c6c990c9dab9f91fa76c074d1a4251df2f9e
Author: randgalt <ra...@apache.org>
Date: Wed May 3 14:20:43 2017 -0500
Another big refactoring. NodeName only needs to be used internally by ZPath as all other resolve methods forward to it. This is cleaner.
commit c8a57d5387d9d34287b9e47808f47832e7758b3e
Merge: 37927efa d7d84c55
Author: randgalt <ra...@apache.org>
Date: Wed May 3 08:17:36 2017 -0500
Merge branch 'CURATOR-407' into CURATOR-397
commit 37927efa65ca755407f1032ef45cb46a11372184
Author: randgalt <ra...@apache.org>
Date: Wed May 3 07:42:13 2017 -0500
refactoring
commit 69f1829d0f63badb0e16ff0bd4d53f3c4a8df541
Author: randgalt <ra...@apache.org>
Date: Tue May 2 22:01:29 2017 -0500
added some doc
commit 0917e314b4cbb75e24e86c942b9d6501f84e8572
Author: randgalt <ra...@apache.org>
Date: Tue May 2 21:37:38 2017 -0500
pub-sub example is now working - needs copious docs
commit 713bf4670e494a74b083431929b86dbfe6bd7f75
Author: randgalt <ra...@apache.org>
Date: Tue May 2 20:45:23 2017 -0500
Work-in-progress. Using example sub/pub to flesh out issues and as an integration test. Working on bugs found
commit 1fcb63a5c7f29ddbfaedfdd18273ccabff21990e
Author: randgalt <ra...@apache.org>
Date: Tue May 2 17:32:38 2017 -0500
Working on strongly typed parameters plus an example that uses it
commit 26c7adbb04a8c20a7d04111253c82ddef28e4f26
Merge: ef9df2b7 ed3082ec
Author: randgalt <ra...@apache.org>
Date: Tue May 2 15:27:27 2017 -0500
Merge branch 'master' into CURATOR-397
commit ef9df2b7915ced99e227e2c2cd6f9ec12c7d7309
Author: randgalt <ra...@apache.org>
Date: Tue May 2 12:58:50 2017 -0500
wip
commit 56c6c85a2f26ef914574139e4c1c657b5a7573a5
Author: randgalt <ra...@apache.org>
Date: Tue May 2 01:11:13 2017 -0500
fixed startsWith
commit c3db1810a10e442626ee0c2f759a3f61da19375a
Author: randgalt <ra...@apache.org>
Date: Tue May 2 00:12:02 2017 -0500
Abstraction for creating ZPaths with strongly typed parameters
commit 6e21af2c565d95b7c281dd5211487a0682ab1a1a
Author: randgalt <ra...@apache.org>
Date: Mon May 1 23:07:58 2017 -0500
refactoring
commit 2cbbf999294181dbefeccebabd9c6de867142e2c
Author: randgalt <ra...@apache.org>
Date: Mon May 1 22:56:36 2017 -0500
Major rework of caching. Having the wrapped caches adds little value. Focus on the integrated caching in the modeled client instance
commit e40ed181655a83e705e6d74704d2fa2d1f93a7bc
Merge: 3d593105 35f5d274
Author: randgalt <ra...@apache.org>
Date: Mon May 1 14:55:59 2017 -0500
Merge branch 'CURATOR-3.0' into CURATOR-397
commit 3d593105d852ece596f7a55f312528ed23ccb69f
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 19:09:28 2017 -0500
more work on new caching apis
commit b58d1ccba2878c0b2f12928b4e957f536c24fed8
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 15:43:03 2017 -0500
resurrected TestCachedModeledCuratorFramework
commit 8418c5604cfeb186a3bd94e310a8001ab2bd5ee6
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 15:36:56 2017 -0500
licenses
commit 16fb7b18b77153578dd489d0e1509a11eff0c6f5
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 15:36:18 2017 -0500
Added new method of integrated caching. Needs testing, etc.
commit a4636098f6a6a679c06f337f981fd17d6b383218
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 14:12:18 2017 -0500
make parameter the empty string
commit 70936c8c479558f737d3e86914af58d3855aa964
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 14:08:28 2017 -0500
Added readChildren()
commit 50b054cc6af009c721e859398efcef5023f3cd3b
Author: randgalt <ra...@apache.org>
Date: Sun Apr 30 14:02:07 2017 -0500
removed integrated caching for now - it was getting too cumbersome. I have some other ideas
commit 11cb97036ebc8639bd22799d99569511deac098b
Author: randgalt <ra...@apache.org>
Date: Sat Apr 29 13:52:36 2017 -0500
more examples, added transactions
commit fa6bff6e5b34960fb8df37097561d4b02d47be24
Author: randgalt <ra...@apache.org>
Date: Fri Apr 28 12:11:08 2017 -0500
updated example
commit 38f6306ba109a61709e88b5acad233d0ba8dff26
Author: randgalt <ra...@apache.org>
Date: Wed Apr 26 17:05:39 2017 -0500
Added resolving methods to the model spec too
commit 52b334c30d82227ad7da3559976fa932fbbc5763
Author: randgalt <ra...@apache.org>
Date: Wed Apr 26 16:53:17 2017 -0500
added a resolving version that takes suppliers for parameters
commit 22fdb298db42a10ab74902b623e1696d22c2021f
Author: randgalt <ra...@apache.org>
Date: Wed Apr 26 16:10:59 2017 -0500
Added some tests/fixes
commit 5ebcfa32d7b00d5c2fc2eb4268a46fea2f98083d
Author: randgalt <ra...@apache.org>
Date: Wed Apr 26 15:45:10 2017 -0500
start of a mechanism to have variable/parameterized paths
commit 0cafc9199f3136f4f22703793bd56c73319d3161
Author: randgalt <ra...@apache.org>
Date: Tue Apr 25 14:29:35 2017 -0500
fixed typo bug with the cached values in ZPath - also made them more efficient
commit 3a3e6dd2d90fd2cf70507f247d1dcc1e59204721
Author: randgalt <ra...@apache.org>
Date: Tue Apr 25 13:30:11 2017 -0500
Created/abstracted CuratorModelSpec so that models can be specified independently of a CuratorFramework instance, etc. i.e. they can all be created at startup. The CuratorModelSpec also can generate a Schema object
commit 63225ba7562c48d9ac53bfaa50aa8ff0a790eb9c
Author: randgalt <ra...@apache.org>
Date: Tue Apr 25 12:33:30 2017 -0500
basics of write-through caching added. needs more work and testing
commit 6188fe6cee6b2a433f1c686adc660216a0aa1648
Author: randgalt <ra...@apache.org>
Date: Tue Apr 18 09:43:48 2017 -0500
Added getChildren
commit e512b5ee52bb9515493d0c077728acd6359caabd
Merge: 691f17d0 00ffe779
Author: randgalt <ra...@apache.org>
Date: Mon Apr 17 19:43:32 2017 -0500
Merge branch 'CURATOR-3.0' into CURATOR-397
commit 691f17d034647359a0d4e29f04f674cf589fb00b
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 17:01:03 2017 -0500
update example index
commit 7c8f3fb62f6c9c7042cf07e78df7c00427392278
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 16:58:58 2017 -0500
added more examples
commit 792aef2094e215edf77bf836b0f4dc43bb2cb232
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 14:27:15 2017 -0500
renamed ModeledCuratorFramework so it isn't so long
commit 34c594a8e6c3730f06bb159709265e0d50b389db
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 13:21:49 2017 -0500
fixed some typos and a bad link in the examples help
commit aadb72b62d6c781704f4ac977717c07a86a2694d
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 08:53:05 2017 -0500
cleaned up ModeledCacheEvent definition
commit bf43232bd9f30efa0482203f895e4adb207ab247
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 08:47:02 2017 -0500
doc
commit e0a27daef0b4dd363171b9ffde6eb4230cd8efd6
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 07:03:50 2017 -0500
Make ModeledNodeCache use the same listener as the others
commit 301e989268a81537645bbdc3c71af4166550abd1
Author: randgalt <ra...@apache.org>
Date: Sun Apr 9 06:40:34 2017 -0500
finished initial tests
commit d518417e5c1bd2d5e4cd1a31833a28eb9bc9bc0a
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 23:29:39 2017 -0500
tests
commit 45daa69bbf2a9ca0cfaa4c1ac20d53ce0314feb9
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 23:00:16 2017 -0500
ModeledDetails isn't needed
commit dd390b6e1b100add85dd9d340dfbae9873b84bd0
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 22:35:51 2017 -0500
doc
commit e8c68188d04df30eb91751f09360bb267fbe1c25
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 22:35:33 2017 -0500
doc
commit 019caeea6aba36e77c64073003e66d62aa60b761
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 22:14:06 2017 -0500
added tree cache wrapper
commit 4efc38f3d16d111c3a96c4eb28212b2cc8a08188
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 21:36:07 2017 -0500
wip on wrapping caches
commit 0f0db1c386471dd4babdf3f26b37b1d9056b3f7d
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 15:15:12 2017 -0500
more doc
commit 1dab81b5afedc4a0669312b386936baf7715c080
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 15:03:49 2017 -0500
adding docs
commit 8237fde0aabf7088b6ddcaf56a7fba1f8b1d3441
Author: randgalt <ra...@apache.org>
Date: Sat Apr 8 13:52:06 2017 -0500
WIP - strongly type DSL for Curator
Project: http://git-wip-us.apache.org/repos/asf/curator/repo
Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/0f5d10da
Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/0f5d10da
Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/0f5d10da
Branch: refs/heads/master
Commit: 0f5d10da38626494ae4d0e3339a2a35f270b0edd
Parents: 8b28b12
Author: randgalt <ra...@apache.org>
Authored: Fri Jul 7 22:31:42 2017 -0500
Committer: randgalt <ra...@apache.org>
Committed: Fri Jul 7 22:31:42 2017 -0500
----------------------------------------------------------------------
curator-examples/pom.xml | 23 +-
.../src/main/java/async/AsyncExamples.java | 127 ++++++
.../src/main/java/modeled/ContainerType.java | 68 +++
.../java/modeled/ModeledCuratorExamples.java | 67 +++
.../java/modeled/ModeledCuratorExamplesAlt.java | 52 +++
.../src/main/java/modeled/PersonId.java | 70 +++
.../src/main/java/modeled/PersonModel.java | 120 +++++
.../src/main/java/modeled/PersonModelSpec.java | 46 ++
.../src/main/java/pubsub/Clients.java | 76 ++++
.../src/main/java/pubsub/Publisher.java | 146 ++++++
curator-examples/src/main/java/pubsub/README.md | 93 ++++
.../src/main/java/pubsub/SubPubTest.java | 220 +++++++++
.../src/main/java/pubsub/Subscriber.java | 84 ++++
.../java/pubsub/messages/LocationAvailable.java | 55 +++
.../main/java/pubsub/messages/UserCreated.java | 64 +++
.../src/main/java/pubsub/models/Group.java | 47 ++
.../src/main/java/pubsub/models/Instance.java | 76 ++++
.../main/java/pubsub/models/InstanceType.java | 27 ++
.../src/main/java/pubsub/models/Message.java | 67 +++
.../src/main/java/pubsub/models/Priority.java | 26 ++
.../src/main/resources/log4j.properties | 24 +
.../src/site/confluence/index.confluence | 5 +-
.../curator/framework/api/CreateBuilder.java | 10 +
.../framework/imps/CreateBuilderImpl.java | 17 +-
.../apache/curator/framework/schema/Schema.java | 2 +-
.../framework/recipes/cache/NodeCache.java | 15 +
.../recipes/cache/PathChildrenCache.java | 2 +-
.../apache/curator/test/BaseClassForTests.java | 10 +-
curator-x-async/pom.xml | 18 +
.../org/apache/curator/x/async/AsyncStage.java | 15 +-
.../apache/curator/x/async/AsyncWrappers.java | 297 ++++++++++++
.../curator/x/async/api/AsyncCreateBuilder.java | 28 ++
.../x/async/api/AsyncCuratorFrameworkDsl.java | 1 -
.../x/async/details/AsyncCreateBuilderImpl.java | 21 +
.../x/async/modeled/JacksonModelSerializer.java | 124 +++++
.../x/async/modeled/ModelSerializer.java | 43 ++
.../curator/x/async/modeled/ModelSpec.java | 217 +++++++++
.../x/async/modeled/ModelSpecBuilder.java | 138 ++++++
.../x/async/modeled/ModeledFramework.java | 371 +++++++++++++++
.../async/modeled/ModeledFrameworkBuilder.java | 154 +++++++
.../curator/x/async/modeled/NodeName.java | 39 ++
.../curator/x/async/modeled/Resolvable.java | 48 ++
.../apache/curator/x/async/modeled/ZNode.java | 74 +++
.../apache/curator/x/async/modeled/ZPath.java | 279 ++++++++++++
.../modeled/cached/CachedModeledFramework.java | 123 +++++
.../x/async/modeled/cached/ModeledCache.java | 46 ++
.../modeled/cached/ModeledCacheListener.java | 106 +++++
.../details/CachedModeledFrameworkImpl.java | 342 ++++++++++++++
.../x/async/modeled/details/ModelSpecImpl.java | 239 ++++++++++
.../x/async/modeled/details/ModelStage.java | 171 +++++++
.../async/modeled/details/ModeledCacheImpl.java | 211 +++++++++
.../modeled/details/ModeledFrameworkImpl.java | 450 +++++++++++++++++++
.../details/VersionedModeledFrameworkImpl.java | 85 ++++
.../x/async/modeled/details/ZNodeImpl.java | 56 +++
.../x/async/modeled/details/ZPathImpl.java | 289 ++++++++++++
.../x/async/modeled/typed/TypedModelSpec.java | 87 ++++
.../x/async/modeled/typed/TypedModelSpec0.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec10.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec2.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec3.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec4.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec5.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec6.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec7.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec8.java | 61 +++
.../x/async/modeled/typed/TypedModelSpec9.java | 61 +++
.../modeled/typed/TypedModeledFramework.java | 93 ++++
.../modeled/typed/TypedModeledFramework0.java | 63 +++
.../modeled/typed/TypedModeledFramework10.java | 63 +++
.../modeled/typed/TypedModeledFramework2.java | 63 +++
.../modeled/typed/TypedModeledFramework3.java | 63 +++
.../modeled/typed/TypedModeledFramework4.java | 63 +++
.../modeled/typed/TypedModeledFramework5.java | 63 +++
.../modeled/typed/TypedModeledFramework6.java | 63 +++
.../modeled/typed/TypedModeledFramework7.java | 63 +++
.../modeled/typed/TypedModeledFramework8.java | 63 +++
.../modeled/typed/TypedModeledFramework9.java | 63 +++
.../x/async/modeled/typed/TypedZPath.java | 92 ++++
.../x/async/modeled/typed/TypedZPath0.java | 52 +++
.../x/async/modeled/typed/TypedZPath10.java | 52 +++
.../x/async/modeled/typed/TypedZPath2.java | 52 +++
.../x/async/modeled/typed/TypedZPath3.java | 52 +++
.../x/async/modeled/typed/TypedZPath4.java | 52 +++
.../x/async/modeled/typed/TypedZPath5.java | 52 +++
.../x/async/modeled/typed/TypedZPath6.java | 52 +++
.../x/async/modeled/typed/TypedZPath7.java | 52 +++
.../x/async/modeled/typed/TypedZPath8.java | 52 +++
.../x/async/modeled/typed/TypedZPath9.java | 52 +++
.../x/async/modeled/versioned/Versioned.java | 69 +++
.../versioned/VersionedModeledFramework.java | 56 +++
.../src/site/confluence/async.confluence | 212 +++++++++
.../src/site/confluence/index.confluence | 213 +--------
.../confluence/modeled-components.confluence | 186 ++++++++
.../site/confluence/modeled-typed.confluence | 89 ++++
.../src/site/confluence/modeled.confluence | 48 ++
curator-x-async/src/site/site.xml | 6 +-
.../x/async/CompletableBaseClassForTests.java | 65 +++
.../curator/x/async/TestAsyncWrappers.java | 73 +++
.../curator/x/async/TestBasicOperations.java | 35 +-
.../modeled/TestCachedModeledFramework.java | 167 +++++++
.../x/async/modeled/TestModeledFramework.java | 178 ++++++++
.../async/modeled/TestModeledFrameworkBase.java | 64 +++
.../curator/x/async/modeled/TestZPath.java | 126 ++++++
.../x/async/modeled/models/TestModel.java | 115 +++++
.../x/async/modeled/models/TestNewerModel.java | 137 ++++++
.../x/async/modeled/models/TestSimpleModel.java | 84 ++++
.../src/test/resources/log4j.properties | 27 ++
curator-x-rpc/src/site/site.xml | 2 +-
pom.xml | 12 +
src/site/site.xml | 4 +-
110 files changed, 9380 insertions(+), 244 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/pom.xml
----------------------------------------------------------------------
diff --git a/curator-examples/pom.xml b/curator-examples/pom.xml
index f612cc2..cc65570 100644
--- a/curator-examples/pom.xml
+++ b/curator-examples/pom.xml
@@ -50,9 +50,30 @@
</dependency>
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-x-async</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
- <scope>test</scope>
</dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/async/AsyncExamples.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/async/AsyncExamples.java b/curator-examples/src/main/java/async/AsyncExamples.java
new file mode 100644
index 0000000..43db219
--- /dev/null
+++ b/curator-examples/src/main/java/async/AsyncExamples.java
@@ -0,0 +1,127 @@
+/**
+ * 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 async;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncEventException;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.zookeeper.WatchedEvent;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * Examples using the asynchronous DSL
+ */
+public class AsyncExamples
+{
+ public static AsyncCuratorFramework wrap(CuratorFramework client)
+ {
+ // wrap a CuratorFramework instance so that it can be used async.
+ // do this once and re-use the returned AsyncCuratorFramework instance
+ return AsyncCuratorFramework.wrap(client);
+ }
+
+ public static void create(CuratorFramework client, String path, byte[] payload)
+ {
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance
+
+ // create a node at the given path with the given payload asynchronously
+ async.create().forPath(path, payload).whenComplete((name, exception) -> {
+ if ( exception != null )
+ {
+ // there was a problem
+ exception.printStackTrace();
+ }
+ else
+ {
+ System.out.println("Created node name is: " + name);
+ }
+ });
+ }
+
+ public static void createThenWatch(CuratorFramework client, String path)
+ {
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance
+
+ // this example shows to asynchronously use watchers for both event
+ // triggering and connection problems. If you don't need to be notified
+ // of connection problems, use the simpler approach shown in createThenWatchSimple()
+
+ // create a node at the given path with the given payload asynchronously
+ // then watch the created node
+ async.create().forPath(path).whenComplete((name, exception) -> {
+ if ( exception != null )
+ {
+ // there was a problem creating the node
+ exception.printStackTrace();
+ }
+ else
+ {
+ handleWatchedStage(async.watched().checkExists().forPath(path).event());
+ }
+ });
+ }
+
+ public static void createThenWatchSimple(CuratorFramework client, String path)
+ {
+ AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance
+
+ // create a node at the given path with the given payload asynchronously
+ // then watch the created node
+ async.create().forPath(path).whenComplete((name, exception) -> {
+ if ( exception != null )
+ {
+ // there was a problem creating the node
+ exception.printStackTrace();
+ }
+ else
+ {
+ // because "WatchMode.successOnly" is used the watch stage is only triggered when
+ // the EventType is a node event
+ async.with(WatchMode.successOnly).watched().checkExists().forPath(path).event().thenAccept(event -> {
+ System.out.println(event.getType());
+ System.out.println(event);
+ });
+ }
+ });
+ }
+
+ private static void handleWatchedStage(CompletionStage<WatchedEvent> watchedStage)
+ {
+ // async handling of Watchers is complicated because watchers can trigger multiple times
+ // and CompletionStage don't support this behavior
+
+ // thenAccept() handles normal watcher triggering.
+ watchedStage.thenAccept(event -> {
+ System.out.println(event.getType());
+ System.out.println(event);
+ // etc.
+ });
+
+ // exceptionally is called if there is a connection problem in which case
+ // watchers trigger to signal the connection problem. "reset()" must be called
+ // to reset the watched stage
+ watchedStage.exceptionally(exception -> {
+ AsyncEventException asyncEx = (AsyncEventException)exception;
+ asyncEx.printStackTrace(); // handle the error as needed
+ handleWatchedStage(asyncEx.reset());
+ return null;
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ContainerType.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ContainerType.java b/curator-examples/src/main/java/modeled/ContainerType.java
new file mode 100644
index 0000000..a36cfaa
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ContainerType.java
@@ -0,0 +1,68 @@
+/**
+ * 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 modeled;
+
+public class ContainerType
+{
+ private final int typeId;
+
+ public ContainerType()
+ {
+ this(0);
+ }
+
+ public ContainerType(int typeId)
+ {
+ this.typeId = typeId;
+ }
+
+ public int getTypeId()
+ {
+ return typeId;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ ContainerType that = (ContainerType)o;
+
+ return typeId == that.typeId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return typeId;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "ContainerType{" + "typeId=" + typeId + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java b/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
new file mode 100644
index 0000000..e7e363c
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
@@ -0,0 +1,67 @@
+/**
+ * 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 modeled;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZPath;
+import java.util.function.Consumer;
+
+public class ModeledCuratorExamples
+{
+ public static ModeledFramework<PersonModel> wrap(AsyncCuratorFramework client)
+ {
+ JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
+
+ // build a model specification - you can pre-build all the model specifications for your app at startup
+ ModelSpec<PersonModel> modelSpec = ModelSpec.builder(ZPath.parse("/example/path"), serializer).build();
+
+ // wrap a CuratorFramework instance so that it can be used "modeled".
+ // do this once and re-use the returned ModeledFramework instance.
+ // ModeledFramework instances are tied to a given path
+ return ModeledFramework.wrap(client, modelSpec);
+ }
+
+ public static void createOrUpdate(ModeledFramework<PersonModel> modeled, PersonModel model)
+ {
+ // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}"
+ ModeledFramework<PersonModel> atId = modeled.child(model.getId().getId());
+
+ // by default ModeledFramework instances update the node if it already exists
+ // so this will either create or update the node
+ atId.set(model); // note - this is async
+ }
+
+ public static void readPerson(ModeledFramework<PersonModel> modeled, String id, Consumer<PersonModel> receiver)
+ {
+ // read the person with the given ID and asynchronously call the receiver after it is read
+ modeled.child(id).read().whenComplete((person, exception) -> {
+ if ( exception != null )
+ {
+ exception.printStackTrace(); // handle the error
+ }
+ else
+ {
+ receiver.accept(person);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java b/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
new file mode 100644
index 0000000..859a1f1
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
@@ -0,0 +1,52 @@
+/**
+ * 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 modeled;
+
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import java.util.function.Consumer;
+
+public class ModeledCuratorExamplesAlt
+{
+ public static void createOrUpdate(PersonModelSpec modelSpec, PersonModel model)
+ {
+ // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}"
+ ModeledFramework<PersonModel> resolved = modelSpec.resolved(model.getContainerType(), model.getId());
+
+ // by default ModeledFramework instances update the node if it already exists
+ // so this will either create or update the node
+ resolved.set(model); // note - this is async
+ }
+
+ public static void readPerson(PersonModelSpec modelSpec, ContainerType containerType, PersonId id, Consumer<PersonModel> receiver)
+ {
+ ModeledFramework<PersonModel> resolved = modelSpec.resolved(containerType, id);
+
+ // read the person with the given ID and asynchronously call the receiver after it is read
+ resolved.read().whenComplete((person, exception) -> {
+ if ( exception != null )
+ {
+ exception.printStackTrace(); // handle the error
+ }
+ else
+ {
+ receiver.accept(person);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/PersonId.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/PersonId.java b/curator-examples/src/main/java/modeled/PersonId.java
new file mode 100644
index 0000000..eabc286
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/PersonId.java
@@ -0,0 +1,70 @@
+/**
+ * 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 modeled;
+
+import java.util.Objects;
+
+public class PersonId
+{
+ private final String id;
+
+ public PersonId()
+ {
+ this("");
+ }
+
+ public PersonId(String id)
+ {
+ this.id = Objects.requireNonNull(id, "id cannot be null");
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ PersonId personId = (PersonId)o;
+
+ return id.equals(personId.id);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PersonId{" + "id='" + id + '\'' + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/PersonModel.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/PersonModel.java b/curator-examples/src/main/java/modeled/PersonModel.java
new file mode 100644
index 0000000..f9b9102
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/PersonModel.java
@@ -0,0 +1,120 @@
+/**
+ * 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 modeled;
+
+import java.util.Objects;
+
+public class PersonModel
+{
+ private final PersonId id;
+ private final ContainerType containerType;
+ private final String firstName;
+ private final String lastName;
+ private final int age;
+
+ public PersonModel()
+ {
+ this(new PersonId(), new ContainerType(), null, null, 0);
+ }
+
+ public PersonModel(PersonId id, ContainerType containerType, String firstName, String lastName, int age)
+ {
+ this.id = Objects.requireNonNull(id, "id cannot be null");
+ this.containerType = Objects.requireNonNull(containerType, "containerType cannot be null");
+ this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null");
+ this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null");
+ this.age = age;
+ }
+
+ public PersonId getId()
+ {
+ return id;
+ }
+
+ public ContainerType getContainerType()
+ {
+ return containerType;
+ }
+
+ public String getFirstName()
+ {
+ return firstName;
+ }
+
+ public String getLastName()
+ {
+ return lastName;
+ }
+
+ public int getAge()
+ {
+ return age;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ PersonModel that = (PersonModel)o;
+
+ if ( age != that.age )
+ {
+ return false;
+ }
+ if ( !id.equals(that.id) )
+ {
+ return false;
+ }
+ if ( !containerType.equals(that.containerType) )
+ {
+ return false;
+ }
+ //noinspection SimplifiableIfStatement
+ if ( !firstName.equals(that.firstName) )
+ {
+ return false;
+ }
+ return lastName.equals(that.lastName);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = id.hashCode();
+ result = 31 * result + containerType.hashCode();
+ result = 31 * result + firstName.hashCode();
+ result = 31 * result + lastName.hashCode();
+ result = 31 * result + age;
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PersonModel{" + "id=" + id + ", containerType=" + containerType + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/PersonModelSpec.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/PersonModelSpec.java b/curator-examples/src/main/java/modeled/PersonModelSpec.java
new file mode 100644
index 0000000..f90f616
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/PersonModelSpec.java
@@ -0,0 +1,46 @@
+/**
+ * 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 modeled;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZPath;
+
+public class PersonModelSpec
+{
+ private final AsyncCuratorFramework client;
+ private final ModelSpec<PersonModel> modelSpec;
+
+ public PersonModelSpec(AsyncCuratorFramework client)
+ {
+ this.client = client;
+
+ JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
+ ZPath path = ZPath.parseWithIds("/example/{id}/path/{id}");
+ modelSpec = ModelSpec.builder(path, serializer).build();
+ }
+
+ public ModeledFramework<PersonModel> resolved(ContainerType containerType, PersonId personId)
+ {
+ ModelSpec<PersonModel> resolved = modelSpec.resolved(containerType.getTypeId(), personId.getId());
+ return ModeledFramework.wrap(client, resolved);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Clients.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Clients.java b/curator-examples/src/main/java/pubsub/Clients.java
new file mode 100644
index 0000000..c0a8136
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Clients.java
@@ -0,0 +1,76 @@
+/**
+ * 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;
+
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import org.apache.zookeeper.CreateMode;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Priority;
+import java.util.concurrent.TimeUnit;
+
+public class Clients
+{
+ /**
+ * A client template for LocationAvailable instances
+ */
+ public static final TypedModeledFramework2<LocationAvailable, Group, Priority> locationAvailableClient = TypedModeledFramework2.from(
+ ModeledFramework.builder(),
+ builder(LocationAvailable.class),
+ "/root/pubsub/messages/locations/{group}/{priority}/{id}"
+ );
+
+ /**
+ * A client template for UserCreated instances
+ */
+ public static final TypedModeledFramework2<UserCreated, Group, Priority> userCreatedClient = TypedModeledFramework2.from(
+ ModeledFramework.builder(),
+ builder(UserCreated.class),
+ "/root/pubsub/messages/users/{group}/{priority}/{id}"
+ );
+
+ /**
+ * A client template for Instance instances
+ */
+ public static final TypedModeledFramework<Instance, InstanceType> instanceClient = TypedModeledFramework.from(
+ ModeledFramework.builder(),
+ builder(Instance.class),
+ "/root/pubsub/instances/{instance-type}/{id}"
+ );
+
+ private static <T> ModelSpecBuilder<T> builder(Class<T> clazz)
+ {
+ return ModelSpec.builder(JacksonModelSerializer.build(clazz))
+ .withTtl(TimeUnit.MINUTES.toMillis(10)) // for our pub-sub example, messages are valid for 10 minutes
+ .withCreateMode(CreateMode.PERSISTENT_WITH_TTL)
+ ;
+ }
+
+ private Clients()
+ {
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Publisher.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Publisher.java b/curator-examples/src/main/java/pubsub/Publisher.java
new file mode 100644
index 0000000..5c8f7af
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Publisher.java
@@ -0,0 +1,146 @@
+/**
+ * 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;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class Publisher
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final AsyncCuratorFramework client;
+
+ public Publisher(AsyncCuratorFramework client)
+ {
+ this.client = Objects.requireNonNull(client, "client cannot be null");
+ }
+
+ /**
+ * Publish the given instance using the Instance client template
+ *
+ * @param instance instance to publish
+ */
+ public void publishInstance(Instance instance)
+ {
+ ModeledFramework<Instance> resolvedClient = Clients.instanceClient.resolved(client, instance.getType());
+ resolvedClient.set(instance).exceptionally(e -> {
+ log.error("Could not publish instance: " + instance, e);
+ return null;
+ });
+ }
+
+ /**
+ * Publish the given instances using the Instance client template in a transaction
+ *
+ * @param instances instances to publish
+ */
+ public void publishInstances(List<Instance> instances)
+ {
+ List<CuratorOp> operations = instances.stream()
+ .map(instance -> Clients.instanceClient
+ .resolved(client, instance.getType())
+ .createOp(instance)
+ )
+ .collect(Collectors.toList());
+ client.transaction().forOperations(operations).exceptionally(e -> {
+ log.error("Could not publish instances: " + instances, e);
+ return null;
+ });
+ }
+
+ /**
+ * Publish the given LocationAvailable using the LocationAvailable client template
+ *
+ * @param group group
+ * @param locationAvailable message to publish
+ */
+ public void publishLocationAvailable(Group group, LocationAvailable locationAvailable)
+ {
+ publishMessage(Clients.locationAvailableClient, group, locationAvailable);
+ }
+
+ /**
+ * Publish the given UserCreated using the UserCreated client template
+ *
+ * @param group group
+ * @param userCreated message to publish
+ */
+ public void publishUserCreated(Group group, UserCreated userCreated)
+ {
+ publishMessage(Clients.userCreatedClient, group, userCreated);
+ }
+
+ /**
+ * Publish the given LocationAvailables using the LocationAvailable client template in a transaction
+ *
+ * @param group group
+ * @param locationsAvailable messages to publish
+ */
+ public void publishLocationsAvailable(Group group, List<LocationAvailable> locationsAvailable)
+ {
+ publishMessages(Clients.locationAvailableClient, group, locationsAvailable);
+ }
+
+ /**
+ * Publish the given UserCreateds using the UserCreated client template in a transaction
+ *
+ * @param group group
+ * @param usersCreated messages to publish
+ */
+ public void publishUsersCreated(Group group, List<UserCreated> usersCreated)
+ {
+ publishMessages(Clients.userCreatedClient, group, usersCreated);
+ }
+
+ private <T extends Message> void publishMessage(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, T message)
+ {
+ ModeledFramework<T> resolvedClient = typedClient.resolved(client, group, message.getPriority());
+ resolvedClient.set(message).exceptionally(e -> {
+ log.error("Could not publish message: " + message, e);
+ return null;
+ });
+ }
+
+ private <T extends Message> void publishMessages(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, List<T> messages)
+ {
+ List<CuratorOp> operations = messages.stream()
+ .map(message -> typedClient
+ .resolved(client, group, message.getPriority())
+ .createOp(message)
+ )
+ .collect(Collectors.toList());
+ client.transaction().forOperations(operations).exceptionally(e -> {
+ log.error("Could not publish messages: " + messages, e);
+ return null;
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/README.md
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/README.md b/curator-examples/src/main/java/pubsub/README.md
new file mode 100644
index 0000000..f6d5971
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/README.md
@@ -0,0 +1,93 @@
+# Pub-Sub Example
+This example models a publish and subscribe system (note: it is not meant for production) using
+the strongly typed modeled APIs in Apache Curator.
+
+## Design Notes
+
+In this example, there are three models that can be published: `Instance`, `LocationAvailable`
+and `UserCreated`. Instances have an `InstanceType`; LocationAvailable and UserCreated both have
+a `Priority` and are associated with a `Group`. (Note: these names/objects are meant for
+illustrative purposes only and are completely contrived)
+
+Each model is stored at a unique path in ZooKeeper:
+
+* Instance: `/root/pubsub/instances/TYPE/ID`
+* LocationAvailable: `/root/pubsub/messages/locations/GROUP/PRIORITY/ID`
+* UserCreated: `/root/pubsub/messages/users/GROUP/PRIORITY/ID`
+
+All models are stored using a TTL so that they automatically get deleted after 10 minutes.
+
+## Clients
+
+This example uses the "typed" models (`TypedModelSpec`, etc.). The typed paths, models and
+clients are meant to be created early in your application and re-used as needed. Thus, you
+can model your ZooKeeper usage and the rest of your application can use them without worrying
+about correct paths, types, etc.
+
+`TypedModeledFramework` is a template that produces a `ModeledFramework` by applying
+parameters to the `TypedZPath` in the contained `TypedModelSpec`. Curator provides variants
+that accept from 1 to 10 parameters (`TypedModeledFramework`, `TypedModeledFramework2`,
+`TypedModeledFramework3`, etc.).
+
+In this example, the TypedModeledFrameworks are defined in `Clients.java`. E.g.
+
+```
+public static final TypedModeledFramework2<LocationAvailable, Group, Priority> locationAvailableClient =
+ TypedModeledFramework2.from(
+ ModeledFramework.builder(),
+ builder(LocationAvailable.class),
+ "/root/pubsub/messages/locations/{group}/{priority}"
+ );
+```
+
+## Publisher
+
+`Publisher.java` shows how to use the ModeledFramework to write models. There are methods to
+write single instances and to write lists of instances in a transaction. Each publish method
+resolves the appropriate typed client and then calls its `set()` method with the given model.
+
+## Subscriber
+
+`Subscriber.java` uses CachedModeledFrameworks to listen for changes on the parent nodes for
+all of the models in this example. Each of the methods resolves the appropriate typed client
+and then starts the cache (via `cached()`).
+
+## SubPubTest
+
+`SubPubTest.java` is a class that exercises this example.
+
+* `start()` uses `Subscriber` to start a `CachedModeledFramework` for each combination of
+the Instance + InstanceType, LocationAvailable + Group + Priority, and UserCreated + Group + Priority. It then adds a simple listener to each cache that merely prints the class name
+and path whenever an update occurs (see `generalListener()`).
+* `start()` also starts a scheduled task that runs every second. This task calls
+`publishSomething()`
+* `publishSomething()` randomly publishes either a single Instance, LocationAvailable,
+UserCreated or a list of those.
+
+`SubPubTest.java` has a `main()` method. When you run you should see something similar to this:
+
+```
+Publishing 9 instances
+Subscribed Instance @ /root/pubsub/instances/proxy/1
+Subscribed Instance @ /root/pubsub/instances/web/2
+Subscribed Instance @ /root/pubsub/instances/cache/4
+Subscribed Instance @ /root/pubsub/instances/proxy/9
+Subscribed Instance @ /root/pubsub/instances/database/3
+Subscribed Instance @ /root/pubsub/instances/cache/5
+Subscribed Instance @ /root/pubsub/instances/database/6
+Subscribed Instance @ /root/pubsub/instances/cache/7
+Subscribed Instance @ /root/pubsub/instances/cache/8
+Publishing 1 userCreated
+Subscribed UserCreated @ /root/pubsub/messages/users/main/high/10
+Publishing 9 locationsAvailable
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/low/11
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/12
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/13
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/14
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/16
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/high/15
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/17
+...
+```
+
+It runs for 1 minute and then exits.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/SubPubTest.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/SubPubTest.java b/curator-examples/src/main/java/pubsub/SubPubTest.java
new file mode 100644
index 0000000..354d568
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/SubPubTest.java
@@ -0,0 +1,220 @@
+/**
+ * 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;
+
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.TestingServer;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.cached.ModeledCacheListener;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Priority;
+import java.io.Closeable;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class SubPubTest implements Closeable
+{
+ private final TestingServer testingServer;
+ private final AsyncCuratorFramework client;
+ private final ScheduledExecutorService executorService;
+ private final List<CachedModeledFramework<Instance>> instanceSubscribers = new ArrayList<>();
+ private final List<CachedModeledFramework<LocationAvailable>> locationAvailableSubscribers = new ArrayList<>();
+ private final List<CachedModeledFramework<UserCreated>> userCreatedSubscribers = new ArrayList<>();
+
+ private static final AtomicLong nextId = new AtomicLong(1);
+
+ // arrays of random values used for this example
+ private static final Group[] groups = {new Group("main"), new Group("admin")};
+ private static final String[] hostnames = {"host1", "host2", "host3"};
+ private static final Integer[] ports = {80, 443, 9999};
+ private static final String[] locations = {"dc1", "dc2", "eu", "us"};
+ private static final Duration[] durations = {Duration.ofSeconds(1), Duration.ofMinutes(1), Duration.ofHours(1)};
+ private static final String[] positions = {"worker", "manager", "executive"};
+
+ public static void main(String[] args)
+ {
+ try ( SubPubTest subPubTest = new SubPubTest() )
+ {
+ subPubTest.start();
+ TimeUnit.MINUTES.sleep(1); // run the test for a minute then exit
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public SubPubTest() throws Exception
+ {
+ this.testingServer = new TestingServer();
+ client = AsyncCuratorFramework.wrap(CuratorFrameworkFactory.newClient(testingServer.getConnectString(), new RetryOneTime(1)));
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ }
+
+ public void start()
+ {
+ client.unwrap().start();
+
+ Publisher publisher = new Publisher(client);
+ Subscriber subscriber = new Subscriber(client);
+
+ // start a subscriber/cache for Instances of each InstanceType
+ instanceSubscribers.addAll(
+ Arrays.stream(InstanceType.values())
+ .map(subscriber::startInstanceSubscriber)
+ .collect(Collectors.toList())
+ );
+
+ // start a subscriber/cache for LocationAvailables of each combination of Group and Priority
+ locationAvailableSubscribers.addAll(
+ Arrays.stream(Priority.values())
+ .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startLocationAvailableSubscriber(group, priority)))
+ .collect(Collectors.toList())
+ );
+
+ // start a subscriber/cache for UserCreateds of each combination of Group and Priority
+ userCreatedSubscribers.addAll(
+ Arrays.stream(Priority.values())
+ .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startUserCreatedSubscriber(group, priority)))
+ .collect(Collectors.toList())
+ );
+
+ // add listeners for each of the caches
+ instanceSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+ locationAvailableSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+ userCreatedSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+
+ // schedule the publisher task once a second
+ executorService.scheduleAtFixedRate(() -> publishSomething(publisher), 1, 1, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ executorService.shutdownNow();
+ try
+ {
+ executorService.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ catch ( InterruptedException ignore )
+ {
+ Thread.currentThread().interrupt();
+ }
+
+ userCreatedSubscribers.forEach(CachedModeledFramework::close);
+ locationAvailableSubscribers.forEach(CachedModeledFramework::close);
+ instanceSubscribers.forEach(CachedModeledFramework::close);
+ client.unwrap().close();
+ testingServer.close();
+ }
+
+ private void publishSomething(Publisher publisher)
+ {
+ // randomly do some publishing - either single items or lists of items in a transaction
+ switch ( ThreadLocalRandom.current().nextInt(6) )
+ {
+ case 0:
+ {
+ Instance instance = new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports));
+ System.out.println("Publishing 1 instance");
+ publisher.publishInstance(instance);
+ break;
+ }
+
+ case 1:
+ {
+ List<Instance> instances = IntStream.range(1, 10)
+ .mapToObj(__ -> new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports)))
+ .collect(Collectors.toList());
+ System.out.println(String.format("Publishing %d instances", instances.size()));
+ publisher.publishInstances(instances);
+ break;
+ }
+
+ case 2:
+ {
+ LocationAvailable locationAvailable = new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations));
+ System.out.println("Publishing 1 locationAvailable");
+ publisher.publishLocationAvailable(random(groups), locationAvailable);
+ break;
+ }
+
+ case 3:
+ {
+ List<LocationAvailable> locationsAvailable = IntStream.range(1, 10)
+ .mapToObj(__ -> new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations)))
+ .collect(Collectors.toList());
+ System.out.println(String.format("Publishing %d locationsAvailable", locationsAvailable.size()));
+ publisher.publishLocationsAvailable(random(groups), locationsAvailable);
+ break;
+ }
+
+ case 4:
+ {
+ UserCreated userCreated = new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions));
+ System.out.println("Publishing 1 userCreated");
+ publisher.publishUserCreated(random(groups), userCreated);
+ break;
+ }
+
+ case 5:
+ {
+ List<UserCreated> usersCreated = IntStream.range(1, 10)
+ .mapToObj(__ -> new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions)))
+ .collect(Collectors.toList());
+ System.out.println(String.format("Publishing %d usersCreated", usersCreated.size()));
+ publisher.publishUsersCreated(random(groups), usersCreated);
+ break;
+ }
+ }
+ }
+
+ private <T> ModeledCacheListener<T> generalListener()
+ {
+ return (type, path, stat, model) -> System.out.println(String.format("Subscribed %s @ %s", model.getClass().getSimpleName(), path));
+ }
+
+ @SafeVarargs
+ private final <T> T random(T... tab)
+ {
+ int index = ThreadLocalRandom.current().nextInt(tab.length);
+ return tab[index];
+ }
+
+ private String nextId()
+ {
+ return Long.toString(nextId.getAndIncrement());
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Subscriber.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Subscriber.java b/curator-examples/src/main/java/pubsub/Subscriber.java
new file mode 100644
index 0000000..94a6247
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Subscriber.java
@@ -0,0 +1,84 @@
+/**
+ * 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;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Message;
+import pubsub.models.Priority;
+
+public class Subscriber
+{
+ private final AsyncCuratorFramework client;
+
+ public Subscriber(AsyncCuratorFramework client)
+ {
+ this.client = client;
+ }
+
+ /**
+ * Start a subscriber (a CachedModeledFramework instance) using the LocationAvailable client template
+ *
+ * @param group group to listen for
+ * @param priority priority to listen for
+ * @return CachedModeledFramework instance (already started)
+ */
+ public CachedModeledFramework<LocationAvailable> startLocationAvailableSubscriber(Group group, Priority priority)
+ {
+ return startSubscriber(Clients.locationAvailableClient, group, priority);
+ }
+
+ /**
+ * Start a subscriber (a CachedModeledFramework instance) using the UserCreated client template
+ *
+ * @param group group to listen for
+ * @param priority priority to listen for
+ * @return CachedModeledFramework instance (already started)
+ */
+ public CachedModeledFramework<UserCreated> startUserCreatedSubscriber(Group group, Priority priority)
+ {
+ return startSubscriber(Clients.userCreatedClient, group, priority);
+ }
+
+ /**
+ * Start a subscriber (a CachedModeledFramework instance) using the Instance client template
+ *
+ * @param instanceType type to listen for
+ * @return CachedModeledFramework instance (already started)
+ */
+ public CachedModeledFramework<Instance> startInstanceSubscriber(InstanceType instanceType)
+ {
+ CachedModeledFramework<Instance> resolved = Clients.instanceClient.resolved(client, instanceType).cached();
+ resolved.start();
+ return resolved;
+ }
+
+ private <T extends Message> CachedModeledFramework<T> startSubscriber(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, Priority priority)
+ {
+ CachedModeledFramework<T> resolved = typedClient.resolved(client, group, priority).cached();
+ resolved.start();
+ return resolved;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java b/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
new file mode 100644
index 0000000..dd90107
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
@@ -0,0 +1,55 @@
+/**
+ * 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.messages;
+
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.time.Duration;
+import java.util.Objects;
+
+public class LocationAvailable extends Message
+{
+ private final String name;
+ private final Duration availableUntil;
+
+ public LocationAvailable()
+ {
+ this(Priority.low, "", Duration.ZERO);
+ }
+
+ public LocationAvailable(Priority priority, String name, Duration availableUntil)
+ {
+ super(priority);
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null");
+ }
+
+ public LocationAvailable(String id, Priority priority, String name, Duration availableUntil)
+ {
+ super(id, priority);
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null");
+ }
+
+ @Override
+ public String toString()
+ {
+ return "LocationAvailable{" + "name='" + name + '\'' + ", availableUntil=" + availableUntil + "} " + super.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/messages/UserCreated.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/messages/UserCreated.java b/curator-examples/src/main/java/pubsub/messages/UserCreated.java
new file mode 100644
index 0000000..bf753a8
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/messages/UserCreated.java
@@ -0,0 +1,64 @@
+/**
+ * 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.messages;
+
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.util.Objects;
+
+public class UserCreated extends Message
+{
+ private final String name;
+ private final String position;
+
+ public UserCreated()
+ {
+ this(Priority.low, "","");
+ }
+
+ public UserCreated(Priority priority, String name, String position)
+ {
+ super(priority);
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.position = Objects.requireNonNull(position, "position cannot be null");
+ }
+
+ public UserCreated(String id, Priority priority, String name, String position)
+ {
+ super(id, priority);
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.position = Objects.requireNonNull(position, "position cannot be null");
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getPosition()
+ {
+ return position;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "UserCreated{" + "name='" + name + '\'' + ", position='" + position + '\'' + "} " + super.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Group.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Group.java b/curator-examples/src/main/java/pubsub/models/Group.java
new file mode 100644
index 0000000..07d149f
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Group.java
@@ -0,0 +1,47 @@
+/**
+ * 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;
+
+import org.apache.curator.x.async.modeled.NodeName;
+
+public class Group implements NodeName
+{
+ private final String groupName;
+
+ public Group()
+ {
+ this("");
+ }
+
+ public Group(String groupName)
+ {
+ this.groupName = groupName;
+ }
+
+ public String getGroupName()
+ {
+ return groupName;
+ }
+
+ @Override
+ public String nodeName()
+ {
+ return groupName;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Instance.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Instance.java b/curator-examples/src/main/java/pubsub/models/Instance.java
new file mode 100644
index 0000000..981f113
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Instance.java
@@ -0,0 +1,76 @@
+/**
+ * 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;
+
+import org.apache.curator.x.async.modeled.NodeName;
+import java.util.Objects;
+import java.util.UUID;
+
+public class Instance implements NodeName
+{
+ private final String id;
+ private final InstanceType type;
+ private final String hostname;
+ private final int port;
+
+ public Instance()
+ {
+ this(UUID.randomUUID().toString(), InstanceType.proxy, "", 0);
+ }
+
+ public Instance(String id, InstanceType type, String hostname, int port)
+ {
+ this.id = Objects.requireNonNull(id, "id cannot be null");
+ this.type = Objects.requireNonNull(type, "type cannot be null");
+ this.hostname = Objects.requireNonNull(hostname, "hostname cannot be null");
+ this.port = port;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public InstanceType getType()
+ {
+ return type;
+ }
+
+ public String getHostname()
+ {
+ return hostname;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ @Override
+ public String nodeName()
+ {
+ return id;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Instance{" + "id='" + id + '\'' + ", type=" + type + ", hostname='" + hostname + '\'' + ", port=" + port + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/InstanceType.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/InstanceType.java b/curator-examples/src/main/java/pubsub/models/InstanceType.java
new file mode 100644
index 0000000..176048c
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/InstanceType.java
@@ -0,0 +1,27 @@
+/**
+ * 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 InstanceType
+{
+ database,
+ cache,
+ web,
+ proxy
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Message.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Message.java b/curator-examples/src/main/java/pubsub/models/Message.java
new file mode 100644
index 0000000..7d92e99
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Message.java
@@ -0,0 +1,67 @@
+/**
+ * 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;
+
+import org.apache.curator.x.async.modeled.NodeName;
+import java.util.Objects;
+import java.util.UUID;
+
+public abstract class Message implements NodeName
+{
+ private final String id;
+ private final Priority priority;
+
+ protected Message()
+ {
+ this(UUID.randomUUID().toString(), Priority.low);
+ }
+
+ protected Message(Priority priority)
+ {
+ this(UUID.randomUUID().toString(), priority);
+ }
+
+ protected Message(String id, Priority priority)
+ {
+ this.id = Objects.requireNonNull(id, "id cannot be null");
+ this.priority = Objects.requireNonNull(priority, "messageType cannot be null");
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public Priority getPriority()
+ {
+ return priority;
+ }
+
+ @Override
+ public String nodeName()
+ {
+ return id;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Message{" + "id='" + id + '\'' + ", priority=" + priority + '}';
+ }
+}
[5/6] curator git commit: Squashed commit of the following:
Posted by ra...@apache.org.
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();
+}
[2/6] curator git commit: Squashed commit of the following:
Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath7.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath7.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath7.java
new file mode 100644
index 0000000..a6eb4d3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath7.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 7 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath7<T1, T2, T3, T4, T5, T6, T7>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7> TypedZPath7<T1, T2, T3, T4, T5, T6, T7> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7> TypedZPath7<T1, T2, T3, T4, T5, T6, T7> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7) -> path.resolved(p1, p2, p3, p4, p5, p6, p7);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath8.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath8.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath8.java
new file mode 100644
index 0000000..68086b5
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath8.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 8 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath8<T1, T2, T3, T4, T5, T6, T7, T8>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8> TypedZPath8<T1, T2, T3, T4, T5, T6, T7, T8> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8> TypedZPath8<T1, T2, T3, T4, T5, T6, T7, T8> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath9.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath9.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath9.java
new file mode 100644
index 0000000..e03c1f1
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedZPath9.java
@@ -0,0 +1,52 @@
+/**
+ * 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.typed;
+
+import org.apache.curator.x.async.modeled.ZPath;
+
+/**
+ * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 9 parameters
+ */
+@FunctionalInterface
+public interface TypedZPath9<T1, T2, T3, T4, T5, T6, T7, T8, T9>
+{
+ ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9);
+
+ /**
+ * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ *
+ * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds}
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8, T9> TypedZPath9<T1, T2, T3, T4, T5, T6, T7, T8, T9> from(String pathWithIds)
+ {
+ return from(ZPath.parseWithIds(pathWithIds));
+ }
+
+ /**
+ * Return a TypedZPath
+ *
+ * @param path path to use
+ * @return TypedZPath
+ */
+ static <T1, T2, T3, T4, T5, T6, T7, T8, T9> TypedZPath9<T1, T2, T3, T4, T5, T6, T7, T8, T9> from(ZPath path)
+ {
+ return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9);
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/Versioned.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/Versioned.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/Versioned.java
new file mode 100644
index 0000000..0bd723b
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/Versioned.java
@@ -0,0 +1,69 @@
+/**
+ * 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.versioned;
+
+/**
+ * A container for a model instance and a version. Can be used with the
+ * {@link org.apache.curator.x.async.modeled.ModeledFramework#versioned()} APIs
+ */
+@FunctionalInterface
+public interface Versioned<T>
+{
+ /**
+ * Returns the contained model
+ *
+ * @return model
+ */
+ T model();
+
+ /**
+ * Returns the version of the model when it was read
+ *
+ * @return version
+ */
+ default int version()
+ {
+ return -1;
+ }
+
+ /**
+ * Return a new Versioned wrapper for the given model and version
+ *
+ * @param model model
+ * @param version version
+ * @return new Versioned wrapper
+ */
+ static <T> Versioned<T> from(T model, int version)
+ {
+ return new Versioned<T>()
+ {
+ @Override
+ public int version()
+ {
+ return version;
+ }
+
+ @Override
+ public T model()
+ {
+ return model;
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/VersionedModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/VersionedModeledFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/VersionedModeledFramework.java
new file mode 100644
index 0000000..c725fd3
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/VersionedModeledFramework.java
@@ -0,0 +1,56 @@
+/**
+ * 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.versioned;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.zookeeper.data.Stat;
+
+public interface VersionedModeledFramework<T>
+{
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#set(Object)
+ */
+ AsyncStage<String> set(Versioned<T> model);
+
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#set(Object, org.apache.zookeeper.data.Stat)
+ */
+ AsyncStage<String> set(Versioned<T> model, Stat storingStatIn);
+
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#read()
+ */
+ AsyncStage<Versioned<T>> read();
+
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#read(org.apache.zookeeper.data.Stat)
+ */
+ AsyncStage<Versioned<T>> read(Stat storingStatIn);
+
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#updateOp(Object)
+ */
+ AsyncStage<Stat> update(Versioned<T> model);
+
+ /**
+ * @see org.apache.curator.x.async.modeled.ModeledFramework#updateOp(Object)
+ */
+ CuratorOp updateOp(Versioned<T> model);
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/confluence/async.confluence
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/confluence/async.confluence b/curator-x-async/src/site/confluence/async.confluence
new file mode 100644
index 0000000..619f5c8
--- /dev/null
+++ b/curator-x-async/src/site/confluence/async.confluence
@@ -0,0 +1,212 @@
+h1. Curator Async
+
+With this DSL you can do asynchronous tasks in a more natural, functional way using
+[Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. For example:
+
+{code}
+// let "client" be a CuratorFramework instance
+AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+async.checkExists().forPath(somePath).thenAccept(stat -> mySuccessOperation(stat));
+{code}
+
+h2. Usage
+
+Note: To use Curator Async, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage.
+
+Create a [[CuratorFramework|../curator\-framework/index.html]] instance in the normal way. You then wrap this instance using
+AsyncCuratorFramework. i.e.
+
+{code}
+// let "client" be a CuratorFramework instance
+AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+{code}
+
+AsyncCuratorFramework has most of the same builder methods that CuratorFramework does with some important
+differences:
+
+* AsyncCuratorFramework builders return {{AsyncStage}} instances
+* AsyncCuratorFramework builders have no checked exceptions
+* Many of the builder methods have been simplified/clarified
+* All builders invoke the asynchronous versions of ZooKeeper APIs
+* Watchers also use CompletionStages \- see below for details
+
+h4. AsyncStage
+
+AsyncStage instances extend Java 8's CompletionStage. CompletionStage objects can be "completed" with a success
+value or an exception. The parameterized type of the AsyncStage will
+be whatever the builder used would naturally return as a success value. E.g. the async getData() builder's AsyncStage is
+parameterized with "byte\[\]".
+
+h4. Watchers
+
+ZooKeeper watchers also get the CompletionStage treatment in Curator Async. To add a watcher, call
+watched() prior to starting the appropriate builders. E.g.
+
+{code}
+async.watched().getData().forPath(path) ...
+{code}
+
+Thus, a data watcher will be set on the specified path. You access the CompletionStage for the watcher
+by using the event() method of AsyncStage. Here is a complete example:
+
+{code}
+async.watched().getData().forPath(path).event().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent));
+{code}
+
+ZooKeeper calls watchers when there is a connection loss. This can make using the CompletionStage
+somewhat complicated (see AsyncEventException below). If you are not interested in watcher connection
+problems, you can tell Curator Async to not send them by calling:
+
+{code}
+// only complete the CompletionStage when the watcher is successfully triggered
+// i.e. don't complete on connection issues
+async.with(WatchMode.successOnly).watched()...
+{code}
+
+h4. AsyncEventException
+
+When an async watcher fails the exception set in the CompletionStage will be of type {{AsyncEventException}}.
+This exception allows you to see the KeeperState that caused the trigger and allows you to reset the
+completion stage. Reset is needed because ZooKeeper temporarily triggers watchers when there is a connection
+event (unless {{WatchMode.successOnly}} is used). However, the watcher stays set for the original operation. Use {{AsyncEventException#reset}}
+to start a new completion stage that will wait on the next trigger of the watcher.
+
+E.g.
+
+{code}
+AsyncStage stage = ...
+stage.event().exceptionally(e -> {
+ AsyncEventException asyncEx = (AsyncEventException)e;
+
+ ... note a connection problem ...
+
+ asyncEx.reset().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent));
+});
+{code}
+
+h4. AsyncResult
+
+As a convenience, you can use {{AsyncResult}} to combine ZooKeeper method value, the ZooKeeper result
+code and any exception in one object allowing you to not worry about exceptional completions. i.e. the {{CompletionStage}}
+returned by {{AsyncResult.of()}} always completes successfully with an AsyncResult object.
+
+AsyncResult has methods to get either the method result (a path, Stat, etc.), a KeeperException code
+or a general exception:
+
+{code}
+Optional<T> getValue();
+
+KeeperException.Code getCode();
+
+Optional<Throwable> getException();
+{code}
+
+Use AsyncResult by wrapping an {{AsyncStage}} value. i.e.
+
+{code}
+CompletionStage<AsyncResult<Stat>> resultStage = AsyncResult.of(async.checkExists().forPath(path));
+resultStage.thenAccept(result -> {
+ if ( result.getValue().isPresent() ) {
+ // ...
+ } else if ( result.getCode() == KeeperException.Code.NOAUTH ) {
+ // ...
+ }
+ // etc.
+});
+{code}
+
+h2. Examples
+
+h4. Create a sequential ZNode
+
+Create a sequential ZNode and, once successfully completed, set a watcher
+on the ZNode. Note: this code does not deal with errors. Should a connection
+problem occur or another exception occur, the completion lambda will never be called.
+
+{code}
+async.create().withMode(PERSISTENT_SEQUENTIAL).forPath(path).thenAccept(actualPath ->
+ async.watched().getData().forPath(actualPath).thenApply(() -> watchTriggered()));
+{code}
+
+----
+
+h4. AsyncStage canonical usage
+
+This is the canonical way to deal with AsyncStage. Use the handle() method which provides
+both the success value and the exception. The exception will be non\-null on error.
+
+{code}
+async.create().withOptions(EnumSet.of(doProtected)).forPath(path).handle((actualPath, exception) -> {
+ if ( exception != null )
+ {
+ // handle problem
+ }
+ else
+ {
+ // actualPath is the path created
+ }
+ return null;
+});
+{code}
+
+----
+
+h4. Simplified usage via AsyncResult
+
+{code}
+AsyncResult.of(async.create().withOptions(EnumSet.of(doProtected)).forPath(path)).thenAccept(result -> {
+ if ( result.getRawValue() != null )
+ {
+ // result.getRawValue() is the path created
+ }
+ else
+ {
+ // ...
+ }
+});
+{code}
+
+----
+
+h4. Using executors
+
+Your completion routines can operate in a separate thread if you provide an executor.
+
+{code}
+async.create().withOptions(EnumSet.of(createParentsIfNeeded)).forPath("/a/b/c")
+ .thenAcceptAsync(path -> handleCreate(path), executor);
+{code}
+
+----
+
+h4. Separate handlers
+
+This example shows specifying separate completion handlers for success and exception.
+
+{code}
+AsyncStage<byte[]> stage = async.getData().forPath("/my/path");
+stage.exceptionally(e -> {
+ if ( e instanceof KeeperException.NoNodeException )
+ {
+ // handle no node
+ }
+ else
+ {
+ // handle other
+ }
+ return null;
+});
+stage.thenAccept(data -> processData(data));
+{code}
+
+----
+
+h4. Synchronous usage
+
+CompletionStage provides a blocking method as well so that you can block to get the result
+of an operation. i.e. this makes it possible to use the async APIs in a synchronous way.
+
+{code}
+// NOTE: get() specifies a checked exception
+async.create().forPath("/foo").toCompletableFuture().get();
+{code}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/confluence/index.confluence
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/confluence/index.confluence b/curator-x-async/src/site/confluence/index.confluence
index 34b6c36..4d81d44 100644
--- a/curator-x-async/src/site/confluence/index.confluence
+++ b/curator-x-async/src/site/confluence/index.confluence
@@ -4,7 +4,7 @@ h2. Packaging
Curator Async is in its own package in Maven Central: curator\-x\-async
-h2. What Is a Curator Async?
+h2. What Is Curator Async?
Curator Async is a [DSL|https://en.wikipedia.org/wiki/Domain-specific_language] that wraps existing
{{CuratorFramework}} instances. This DSL is entirely asynchronous and uses
@@ -13,212 +13,39 @@ mechanism for chaining, composing, etc. Additionally, Curator's original DSL has
and simplified, in particular for operations such as {{create()}}.
With this DSL you can do asynchronous tasks in a more natural, functional way using
-[Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. For example:
-
-{code}
-// let "client" be a CuratorFramework instance
-AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
-async.checkExists().forPath(somePath).thenAccept(stat -> mySuccessOperation(stat));
-{code}
+[Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html].
-h2. Usage
+The Curator Async package also contains a strongly typed DSL and strongly typed Cache Recipe wrappers that
+allows you to map a ZooKeeper path to a serializable class as opposed to raw byte arrays.
-Note: To use Curator Async, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage.
+h2. [[Curator Async|async.html]]
-Create a [[CuratorFramework|../curator\-framework/index.html]] instance in the normal way. You then wrap this instance using
-AsyncCuratorFramework. i.e.
+With this DSL you can do asynchronous tasks in a more natural, functional way using
+[Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. For example:
{code}
// let "client" be a CuratorFramework instance
AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
+async.checkExists().forPath(somePath).thenAccept(stat -> mySuccessOperation(stat));
{code}
-AsyncCuratorFramework has most of the same builder methods that CuratorFramework does with some important
-differences:
-
-* AsyncCuratorFramework builders return {{AsyncStage}} instances
-* AsyncCuratorFramework builders have no checked exceptions
-* Many of the builder methods have been simplified/clarified
-* All builders invoke the asynchronous versions of ZooKeeper APIs
-* Watchers also use CompletionStages \- see below for details
-
-h4. AsyncStage
-
-AsyncStage instances extend Java 8's CompletionStage. CompletionStage objects can be "completed" with a success
-value or an exception. The parameterized type of the AsyncStage will
-be whatever the builder used would naturally return as a success value. E.g. the async getData() builder's AsyncStage is
-parameterized with "byte\[\]".
-
-h4. Watchers
-
-ZooKeeper watchers also get the CompletionStage treatment in Curator Async. To add a watcher, call
-watched() prior to starting the appropriate builders. E.g.
-
-{code}
-async.watched().getData().forPath(path) ...
-{code}
-
-Thus, a data watcher will be set on the specified path. You access the CompletionStage for the watcher
-by using the event() method of AsyncStage. Here is a complete example:
-
-{code}
-async.watched().getData().forPath(path).event().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent));
-{code}
-
-ZooKeeper calls watchers when there is a connection loss. This can make using the CompletionStage
-somewhat complicated (see AsyncEventException below). If you are not interested in watcher connection
-problems, you can tell Curator Async to not send them by calling:
-
-{code}
-// only complete the CompletionStage when the watcher is successfully triggered
-// i.e. don't complete on connection issues
-async.with(WatchMode.successOnly).watched()...
-{code}
-
-h4. AsyncEventException
-
-When an async watcher fails the exception set in the CompletionStage will be of type {{AsyncEventException}}.
-This exception allows you to see the KeeperState that caused the trigger and allows you to reset the
-completion stage. Reset is needed because ZooKeeper temporarily triggers watchers when there is a connection
-event (unless {{WatchMode.successOnly}} is used). However, the watcher stays set for the original operation. Use {{AsyncEventException#reset}}
-to start a new completion stage that will wait on the next trigger of the watcher.
-
-E.g.
-
-{code}
-AsyncStage stage = ...
-stage.event().exceptionally(e -> {
- AsyncEventException asyncEx = (AsyncEventException)e;
-
- ... note a connection problem ...
-
- asyncEx.reset().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent));
-});
-{code}
-
-h4. AsyncResult
-
-As a convenience, you can use {{AsyncResult}} to combine ZooKeeper method value, the ZooKeeper result
-code and any exception in one object allowing you to not worry about exceptional completions. i.e. the {{CompletionStage}}
-returned by {{AsyncResult.of()}} always completes successfully with an AsyncResult object.
-
-AsyncResult has methods to get either the method result (a path, Stat, etc.), a KeeperException code
-or a general exception:
-
-{code}
-Optional<T> getValue();
-
-KeeperException.Code getCode();
-
-Optional<Throwable> getException();
-{code}
-
-Use AsyncResult by wrapping an {{AsyncStage}} value. i.e.
-
-{code}
-CompletionStage<AsyncResult<Stat>> resultStage = AsyncResult.of(async.checkExists().forPath(path));
-resultStage.thenAccept(result -> {
- if ( result.getValue().isPresent() ) {
- // ...
- } else if ( result.getCode() == KeeperException.Code.NOAUTH ) {
- // ...
- }
- // etc.
-});
-{code}
-
-h2. Examples
-
-h4. Create a sequential ZNode
-
-Create a sequential ZNode and, once successfully completed, set a watcher
-on the ZNode. Note: this code does not deal with errors. Should a connection
-problem occur or another exception occur, the completion lambda will never be called.
-
-{code}
-async.create().withMode(PERSISTENT_SEQUENTIAL).forPath(path).thenAccept(actualPath ->
- async.watched().getData().forPath(actualPath).thenApply(() -> watchTriggered()));
-{code}
-
-----
-
-h4. AsyncStage canonical usage
-
-This is the canonical way to deal with AsyncStage. Use the handle() method which provides
-both the success value and the exception. The exception will be non\-null on error.
-
-{code}
-async.create().withOptions(EnumSet.of(doProtected)).forPath(path).handle((actualPath, exception) -> {
- if ( exception != null )
- {
- // handle problem
- }
- else
- {
- // actualPath is the path created
- }
- return null;
-});
-{code}
-
-----
-
-h4. Simplified usage via AsyncResult
-
-{code}
-AsyncResult.of(async.create().withOptions(EnumSet.of(doProtected)).forPath(path)).thenAccept(result -> {
- if ( result.getRawValue() != null )
- {
- // result.getRawValue() is the path created
- }
- else
- {
- // ...
- }
-});
-{code}
-
-----
+See [[Curator Async|async.html]] for details.
-h4. Using executors
+h2. [[Modeled Curator|modeled.html]]
-Your completion routines can operate in a separate thread if you provide an executor.
+This is a strongly typed DSL that allows you to map a Curator\-style client to:
-{code}
-async.create().withOptions(EnumSet.of(createParentsIfNeeded)).forPath("/a/b/c")
- .thenAcceptAsync(path -> handleCreate(path), executor);
-{code}
-
-----
+* A ZooKeeper path (supporting parameterized substitutions)
+* A serializer for the data stored at the path
+* Options for how nodes should be created (sequential, compressed data, ttl, etc.)
+* ACLs for the nodes at the path
+* Options for how to delete nodes (guaranteed, deleting children, etc.)
-h4. Separate handlers
-
-This example shows specifying separate completion handlers for success and exception.
+For example:
{code}
-AsyncStage<byte[]> stage = async.getData().forPath("/my/path");
-stage.exceptionally(e -> {
- if ( e instanceof KeeperException.NoNodeException )
- {
- // handle no node
- }
- else
- {
- // handle other
- }
- return null;
-});
-stage.thenAccept(data -> processData(data));
+ModeledFramework<Foo> modeled = ModeledFramework.wrap(client, fooModelSpec);
+modeled.set(new Foo());
{code}
-----
-
-h4. Synchronous usage
-
-CompletionStage provides a blocking method as well so that you can block to get the result
-of an operation. i.e. this makes it possible to use the async APIs in a synchronous way.
-
-{code}
-// NOTE: get() specifies a checked exception
-async.create().forPath("/foo").toCompletableFuture().get();
-{code}
+See [[Modeled Curator|modeled.html]] for details.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/confluence/modeled-components.confluence
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/confluence/modeled-components.confluence b/curator-x-async/src/site/confluence/modeled-components.confluence
new file mode 100644
index 0000000..62bcdeb
--- /dev/null
+++ b/curator-x-async/src/site/confluence/modeled-components.confluence
@@ -0,0 +1,186 @@
+h1. Modeled Curator \- Components
+
+Modeled Curator components are intended to allow you to model your ZooKeeper usage early in your application
+so that the majority of the code that interacts with ZooKeeper doesn't need to be concerned with
+paths, byte arrays, ACLs, options, etc. The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]]
+can give you some ideas on how to accomplish this.
+
+h2. ZPath
+
+Instead of using raw string paths, Modeled Curator defines the {{ZPath}} interface that abstracts
+ZooKeeper paths. ZPaths can be simple static paths or can contain parameters that can be replaced
+as needed.
+
+To build a simple static path, use:
+
+{code}
+ZPath path = ZPath.parse("/my/static/path");
+{code}
+
+To build a path with parameters, use. {{ZPath.parseWithIds()}} using the value "\{XXXX\}" to
+denote a parameter. You can then use the {{resolve()}} method to replace the parameters. The value
+between "\{\}" can be any value. E.g.
+
+{code}
+ZPath path = ZPath.parseWithIds("/foo/{first param}/bar/{second param}");
+
+...
+
+ZPath resolvedPath = path.resolve(param1, param2);
+{code}
+
+h3. NodeName
+
+Parameters are resolved by calling {{toString()}} on the parameter. You can use {{NodeName}}
+to change this behavior. If a parameter implements {{NodeName}} the {{nodeName()}} method
+is used as the parameter value.
+
+h3. Partial Resolution
+
+Note: ZPaths can be partially resolved. E.g.
+
+{code}
+ZPath path = ZPath.parseWithIds("/foo/{type}/bar/{id}");
+
+...
+
+ZPath partial = path.resolve("standard");
+// partial is now "/foo/standard/bar/{id}"
+{code}
+
+ModeledFramework takes advantage of this. [[See below|#ModeledFramework]] for details.
+
+h2. ModelSpec
+
+A {{ModelSpec}} contains all the metadata needed to operate on a ZooKeeper path:
+
+* A ZPath
+* A serializer for the data stored at the path
+* Options for how nodes should be created (sequential, compressed data, ttl, etc.)
+* ACLs for the nodes at the path
+* Options for how to delete nodes (guaranteed, deleting children, etc.)
+
+ModelSpec instances are created via a builder. The builder sets defaults that should be
+useful for most applications but you can alter any of these as needed.
+
+{code}
+// a standard model spec for the given path and serializer
+// the model spec will have no ACLs and the options:
+// * createParentsAsContainers
+// * setDataIfExists
+// * DeleteOption.guaranteed
+ModelSpec<MyModel> spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)).build();
+{code}
+
+As a convenience, ModelSpec provides {{resolve()}} methods in case the ZPath used has parameters.
+E.g.
+
+{code}
+ZPath path = ZPath.parseWithIds("/foo/{id}/bar/{id}");
+ModelSpec<MyModel> spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)).build();
+
+...
+
+ModelSpec<MyModel> resolvedSpec = spec.resolve(param1, param2);
+{code}
+
+h3. JacksonModelSerializer
+
+A Jackson serializer, {{JacksonModelSerializer}}, is included. However, the Jackson dependency for it is
+specified as "provided" 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.
+
+E.g. for Maven:
+
+{code}
+<dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>XXXX</version>
+</dependency>
+{code}
+
+h2. ModeledFramework
+
+{{ModeledFramework}} ties together all the metadata into a Curator\-style instance that is
+used to perform ZooKeeper operations. E.g.
+
+{code}
+ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec);
+
+...
+
+MyModel instance = ...
+modeledClient.set(instance);
+{code}
+
+The "set" call in the above example is the equivalent of:
+
+{code}
+MyModel instance = ...
+String path = "/foo/bar/" + instance.getId();
+byte[] data = serializer.serialize(instance);
+client.create()
+ .withOptions(Sets.newHashSet(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists))
+ .forPath(path, data);
+{code}
+
+To get a value:
+
+{code}
+ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec);
+
+...
+
+modeledClient.read().whenComplete((value, e) -> {
+ if ( e != null ) {
+ // handle the error
+ } else {
+ // "value" is the MyModel instance
+ }
+});
+{code}
+
+The "read" call in the above example is the equivalent of:
+
+{code}
+String path = "/foo/bar/" + instanceId;
+client.getData().forPath(path).whenComplete((data, e) -> {
+ if ( e != null ) {
+ // handle the error
+ } else {
+ // NOTE: you must deal with possible deserialization problems
+ // caused by clients that write bad data
+ // If all of your code uses ModeledFramework you can guarantee that
+ // the data is always correctly written
+ MyModel model = serializer.deserialize(data);
+ // ...
+ }
+});
+{code}
+
+h3. Partially Resolved ZPaths and Set/Update
+
+ModeledFramework's various {{set}} and {{update}} methods check for unresolved ZPaths. If the current
+modelSpec has an unresolved ZPath when set/update is called, it is automatically resolved using the model
+instance being set/updated. E.g.
+
+{code}
+ZPath path = ZPath.parseWithIds("/root/{type}/instance/{id}");
+ModelSpec<MyModel> modelSpec = ModelSpec.builder(path, serializer);
+ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(modelSpec, client, modelSpec);
+
+...
+
+String currentType = ...
+MyModel model = ...
+modeledClient.resolved(currentType).set(model); // internally, ModeledFramework calls ZPath.resolved()
+ // using "model" as the argument to get the actual ZPath
+{code}
+
+h2. Caching and Typed Parameters
+
+In addition to the above features, Modeled Curator supports [[Integrated Caching|modeled-typed.html]],
+[[Typed Parameters|modeled-typed.html]] and [[Versioning|modeled-typed.html]]. See
+[[Caching and Typed Parameters|modeled-typed.html]] for details.
+
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/confluence/modeled-typed.confluence
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/confluence/modeled-typed.confluence b/curator-x-async/src/site/confluence/modeled-typed.confluence
new file mode 100644
index 0000000..c02ea80
--- /dev/null
+++ b/curator-x-async/src/site/confluence/modeled-typed.confluence
@@ -0,0 +1,89 @@
+h1. Modeled Curator \- Caching, Typed Parameters and Versioning
+
+In addition to its [[main features|modeled-components.html]] Modeled Curator also supports
+integrated caching, typed parameters and versioning.
+
+h2. Caching
+
+{{ModeledFramework}} instances can be wrapped with a facade that uses a Curator cache internally.
+All read operations use this cache instead of making direct ZooKeeper calls. You can also
+listen for node changes. E.g.
+
+{code}
+ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec);
+CachedModeledFramework<MyModel> cached = modeledClient.cached();
+cached.start();
+
+// reads come from the cache
+cached.read().whenComplete(...) ...
+
+cached.listenable.addListener((type, path, stat, model) -> {
+ // type is NODE_ADDED, NODE_UPDATED, etc.
+});
+{code}
+
+h3. Unresolved Paths and Caching
+
+If the last node in the ModelSpec's path is a parameter, CachedModeledFramework will automatically
+listen to the parent path. E.g.
+
+{code}
+ZPath path = ZPath.parseWithIds("/root/instance/{id}");
+ModelSpec<MyModel> modelSpec = ModelSpec.builder(path, serializer);
+ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(modelSpec, client, modelSpec);
+
+CachedModeledFramework<MyModel> cached = modeledClient.cached();
+cached.start(); // automatically listens to "/root/instance" and below
+{code}
+
+h2. Typed Parameters
+
+The "resolve" methods in ZPath et al consume untyped Objects. Ideally, we should be able to
+specify parameters in a strongly typed manner. Modeled Curator's "type" templates provide this. You
+can specify typed parameters for ZPaths, ModelSpecs and ModeledFramework.
+The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]]
+shows how to use typed parameters with ModeledFramework.
+
+Typed interfaces are provided for up to 10 parameters and are named
+{{TypedZPath}}, {{TypedZPath2}}, {{TypedModelSpec}}, {{TypedModelSpec2}}, {{TypedModeledFramework}},
+{{TypedModeledFramework2}}, etc.
+
+Here's an example of a TypedModeledFramework that models a Person and uses two parameters
+to generate the path, a Group and an Organization:
+
+{code}
+TypedModeledFramework2<Person, Group, Organization> clientTemplate = TypedModeledFramework2.from(
+ ModeledFrameworkBuilder.build(),
+ personModelSpec
+);
+
+...
+
+Group group = ...
+Organization organization = ...
+ModeledFramework<Person> modeledClient = clientTemplate.resolve(asyncClient, group, organization);
+client.set(person);
+{code}
+
+TypedZPath and TypedModelSpec work similarly.
+
+h2. Versioning
+
+Modeled Curator supports associating a ZNode version with a model object via
+the {{Versioned}} interface and the {{VersionedModeledFramework}} APIs. To
+read a model along with its ZNode version use:
+
+{code}
+ModeledFramework<Person> client = ...
+
+client.versioned().read().whenComplete((value, e) -> {
+ if ( value != null ) {
+ // value's type is Versioned<Person>
+ Person personModel = value.model();
+ int znodeVersion = value.version();
+ }
+});
+{code}
+
+{{VersionedModeledFramework}} has set/update APIs which automatically use the version
+from the {{Versioned}} instance.
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/confluence/modeled.confluence
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/confluence/modeled.confluence b/curator-x-async/src/site/confluence/modeled.confluence
new file mode 100644
index 0000000..32e8727
--- /dev/null
+++ b/curator-x-async/src/site/confluence/modeled.confluence
@@ -0,0 +1,48 @@
+h1. Modeled Curator
+
+This is a strongly typed DSL that allows you to map a Curator\-style client to:
+
+* A ZooKeeper path (supporting parameterized substitutions)
+* A serializer for the data stored at the path
+* Options for how nodes should be created (sequential, compressed data, ttl, etc.)
+* ACLs for the nodes at the path
+* Options for how to delete nodes (guaranteed, deleting children, etc.)
+
+For example:
+
+{code}
+ModeledFramework<Foo> modeled = ModeledFramework.wrap(client, fooModelSpec);
+modeled.set(new Foo());
+{code}
+
+This ModeledFramework instance knows the path to use, how to serialize the "Foo" instance,
+which create options and ACLs to use, etc.
+
+h2. Background and Usage
+
+Note: To use Modeled Curator, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage.
+You should also be familiar with [[Curator Async|async.html]] as Modeled Curator is based on it.
+
+Modeled Curator consists of the components:
+
+* [[ZPath|modeled-components.html]]
+* [[ModelSpec|modeled-components.html]]
+* [[ModeledFramework|modeled-components.html]]
+
+Additional functionality is provided by:
+
+* [[CachedModeledFramework|modeled-typed.html]]
+* [[Typed Parameter Templates|modeled-typed.html]]
+* [[Versioning|modeled-typed.html]]
+
+h2. Example
+
+A complete example usage of Modeled Curator along with CachedModeledFramework and Typed Parameter Templates
+can be found here: [[https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]].
+
+h2. Details
+
+For more details see:
+
+* [[Components|modeled-components.html]]
+* [[Caching, Typed Parameters and Versioning|modeled-typed.html]]
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/site/site.xml
----------------------------------------------------------------------
diff --git a/curator-x-async/src/site/site.xml b/curator-x-async/src/site/site.xml
index 63fccaa..f78abc7 100644
--- a/curator-x-async/src/site/site.xml
+++ b/curator-x-async/src/site/site.xml
@@ -25,7 +25,11 @@
<link rel="stylesheet" href="../css/site.css" />
<script type="text/javascript">
$(function(){
- $('a[title="Curator RPC Proxy"]').parent().addClass("active");
+ if ( location && location.pathname && location.pathname.endsWith('/index.html') ) {
+ $('a[title="Java 8/Async"]').parent().addClass("active");
+ } else {
+ $('a[title="Strongly Typed Models"]').parent().addClass("active");
+ }
});
</script>
</head>
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
new file mode 100644
index 0000000..232d301
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
@@ -0,0 +1,65 @@
+/**
+ * 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.test.BaseClassForTests;
+import org.apache.curator.test.Timing;
+import org.testng.Assert;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+
+public abstract class CompletableBaseClassForTests extends BaseClassForTests
+{
+ protected static final Timing timing = new Timing();
+
+ protected <T, U> void complete(CompletionStage<T> stage)
+ {
+ complete(stage, (v, e) -> {});
+ }
+
+ protected <T, U> void complete(CompletionStage<T> stage, BiConsumer<? super T, Throwable> handler)
+ {
+ try
+ {
+ stage.handle((v, e) -> {
+ handler.accept(v, e);
+ return null;
+ }).toCompletableFuture().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS);
+ }
+ catch ( InterruptedException e )
+ {
+ Thread.interrupted();
+ }
+ catch ( ExecutionException e )
+ {
+ if ( e.getCause() instanceof AssertionError )
+ {
+ throw (AssertionError)e.getCause();
+ }
+ Assert.fail("get() failed", e);
+ }
+ catch ( TimeoutException e )
+ {
+ Assert.fail("get() timed out");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/TestAsyncWrappers.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/TestAsyncWrappers.java b/curator-x-async/src/test/java/org/apache/curator/x/async/TestAsyncWrappers.java
new file mode 100644
index 0000000..7ce7904
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/TestAsyncWrappers.java
@@ -0,0 +1,73 @@
+/**
+ * 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.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.locks.InterProcessMutex;
+import org.apache.curator.retry.RetryOneTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class TestAsyncWrappers extends CompletableBaseClassForTests
+{
+ @Test
+ public void testBasic()
+ {
+ try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) )
+ {
+ client.start();
+
+ InterProcessMutex lock = new InterProcessMutex(client, "/one/two");
+ complete(AsyncWrappers.lockAsync(lock), (__, e) -> {
+ Assert.assertNull(e);
+ AsyncWrappers.release(lock);
+ });
+ }
+ }
+
+ @Test
+ public void testContention() throws Exception
+ {
+ try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) )
+ {
+ client.start();
+
+ InterProcessMutex lock1 = new InterProcessMutex(client, "/one/two");
+ InterProcessMutex lock2 = new InterProcessMutex(client, "/one/two");
+ CountDownLatch latch = new CountDownLatch(1);
+ AsyncWrappers.lockAsync(lock1).thenAccept(__ -> {
+ latch.countDown(); // don't release the lock
+ });
+ Assert.assertTrue(timing.awaitLatch(latch));
+
+ CountDownLatch latch2 = new CountDownLatch(1);
+ AsyncWrappers.lockAsync(lock2, timing.forSleepingABit().milliseconds(), TimeUnit.MILLISECONDS).exceptionally(e -> {
+ if ( e instanceof AsyncWrappers.TimeoutException )
+ {
+ latch2.countDown(); // lock should still be held
+ }
+ return null;
+ });
+ Assert.assertTrue(timing.awaitLatch(latch2));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
index 1c4f241..0274413 100644
--- a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
@@ -44,9 +44,8 @@ import static org.apache.curator.x.async.api.CreateOption.compress;
import static org.apache.curator.x.async.api.CreateOption.setDataIfExists;
import static org.apache.zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL;
-public class TestBasicOperations extends BaseClassForTests
+public class TestBasicOperations extends CompletableBaseClassForTests
{
- private static final Timing timing = new Timing();
private AsyncCuratorFramework client;
@BeforeMethod
@@ -181,36 +180,4 @@ public class TestBasicOperations extends BaseClassForTests
Assert.assertEquals(v.getCode(), KeeperException.Code.CONNECTIONLOSS);
});
}
-
- private <T, U> void complete(CompletionStage<T> stage)
- {
- complete(stage, (v, e) -> {});
- }
-
- private <T, U> void complete(CompletionStage<T> stage, BiConsumer<? super T, Throwable> handler)
- {
- try
- {
- stage.handle((v, e) -> {
- handler.accept(v, e);
- return null;
- }).toCompletableFuture().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS);
- }
- catch ( InterruptedException e )
- {
- Thread.interrupted();
- }
- catch ( ExecutionException e )
- {
- if ( e.getCause() instanceof AssertionError )
- {
- throw (AssertionError)e.getCause();
- }
- Assert.fail("get() failed", e);
- }
- catch ( TimeoutException e )
- {
- Assert.fail("get() timed out");
- }
- }
}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestCachedModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestCachedModeledFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestCachedModeledFramework.java
new file mode 100644
index 0000000..49821e2
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestCachedModeledFramework.java
@@ -0,0 +1,167 @@
+/**
+ * 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.state.ConnectionState;
+import org.apache.curator.test.Timing;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.cached.ModeledCacheListener;
+import org.apache.curator.x.async.modeled.models.TestModel;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class TestCachedModeledFramework extends TestModeledFrameworkBase
+{
+ @Test
+ public void testThreading()
+ {
+ TestModel model = new TestModel("a", "b", "c", 1, BigInteger.ONE);
+ CachedModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec).cached().asyncDefault();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ client.listenable().addListener((type, path1, stat, model1) -> latch.countDown());
+
+ complete(client.set(model));
+ client.start();
+ try
+ {
+ Assert.assertTrue(new Timing().awaitLatch(latch));
+
+ AtomicReference<Thread> completionThread = new AtomicReference<>();
+ complete(client.read().whenCompleteAsync((s, e) -> completionThread.set((e == null) ? Thread.currentThread() : null)));
+ Assert.assertNotNull(completionThread.get());
+ Assert.assertNotEquals(Thread.currentThread(), completionThread.get(), "Should be different threads");
+ completionThread.set(null);
+
+ complete(client.child("foo").read().whenCompleteAsync((v, e) -> completionThread.set((e != null) ? Thread.currentThread() : null)));
+ Assert.assertNotNull(completionThread.get());
+ Assert.assertNotEquals(Thread.currentThread(), completionThread.get(), "Should be different threads");
+ completionThread.set(null);
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @Test
+ public void testCustomThreading()
+ {
+ AtomicReference<Thread> ourThread = new AtomicReference<>();
+ ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
+ Thread thread = new Thread(r, "testCustomThreading");
+ ourThread.set(thread);
+ return thread;
+ });
+ TestModel model = new TestModel("a", "b", "c", 1, BigInteger.ONE);
+ CachedModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec).cached(executor).asyncDefault();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ client.listenable().addListener((type, path1, stat, model1) -> latch.countDown());
+
+ complete(client.set(model));
+ client.start();
+ try
+ {
+ Assert.assertTrue(new Timing().awaitLatch(latch));
+
+ AtomicReference<Thread> completionThread = new AtomicReference<>();
+ complete(client.read().thenAcceptAsync(s -> completionThread.set(Thread.currentThread())));
+ Assert.assertEquals(ourThread.get(), completionThread.get(), "Should be our thread");
+ completionThread.set(null);
+
+ complete(client.child("foo").read().whenCompleteAsync((v, e) -> completionThread.set((e != null) ? Thread.currentThread() : null)));
+ Assert.assertEquals(ourThread.get(), completionThread.get(), "Should be our thread");
+ completionThread.set(null);
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @Test
+ public void testDownServer() throws IOException
+ {
+ Timing timing = new Timing();
+
+ TestModel model = new TestModel("a", "b", "c", 1, BigInteger.ONE);
+ CachedModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec).cached();
+ Semaphore semaphore = new Semaphore(0);
+ client.listenable().addListener((t, p, s, m) -> semaphore.release());
+
+ client.start();
+ try
+ {
+ client.set(model);
+ Assert.assertTrue(timing.acquireSemaphore(semaphore));
+
+ CountDownLatch latch = new CountDownLatch(1);
+ rawClient.getConnectionStateListenable().addListener((__, state) -> {
+ if ( state == ConnectionState.LOST )
+ {
+ latch.countDown();
+ }
+ });
+ server.stop();
+ Assert.assertTrue(timing.awaitLatch(latch));
+
+ complete(client.read().whenComplete((value, e) -> {
+ Assert.assertNotNull(value);
+ Assert.assertNull(e);
+ }));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @Test
+ public void testPostInitializedFilter()
+ {
+ TestModel model1 = new TestModel("a", "b", "c", 1, BigInteger.ONE);
+ TestModel model2 = new TestModel("d", "e", "e", 1, BigInteger.ONE);
+ CachedModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec).cached();
+ Semaphore semaphore = new Semaphore(0);
+ ModeledCacheListener<TestModel> listener = (t, p, s, m) -> semaphore.release();
+ client.listenable().addListener(listener.postInitializedOnly());
+
+ complete(client.child("1").set(model1)); // set before cache is started
+ client.start();
+ try
+ {
+ Assert.assertFalse(timing.forSleepingABit().acquireSemaphore(semaphore));
+
+ client.child("2").set(model2); // set before cache is started
+ Assert.assertTrue(timing.acquireSemaphore(semaphore));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFramework.java
new file mode 100644
index 0000000..42a9e63
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFramework.java
@@ -0,0 +1,178 @@
+/**
+ * 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.Sets;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.schema.Schema;
+import org.apache.curator.framework.schema.SchemaSet;
+import org.apache.curator.framework.schema.SchemaViolation;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.modeled.models.TestModel;
+import org.apache.curator.x.async.modeled.models.TestNewerModel;
+import org.apache.curator.x.async.modeled.versioned.Versioned;
+import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+public class TestModeledFramework extends TestModeledFrameworkBase
+{
+ @Test
+ public void testCrud()
+ {
+ TestModel rawModel = new TestModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1));
+ TestModel rawModel2 = new TestModel("Wayne", "Rooney", "Old Trafford", 10, BigInteger.valueOf(1));
+ ModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec);
+ AsyncStage<String> stage = client.set(rawModel);
+ Assert.assertNull(stage.event());
+ complete(stage, (s, e) -> Assert.assertNotNull(s));
+ complete(client.read(), (model, e) -> Assert.assertEquals(model, rawModel));
+ complete(client.update(rawModel2));
+ complete(client.read(), (model, e) -> Assert.assertEquals(model, rawModel2));
+ complete(client.delete());
+ complete(client.checkExists(), (stat, e) -> Assert.assertNull(stat));
+ }
+
+ @Test
+ public void testBackwardCompatibility()
+ {
+ TestNewerModel rawNewModel = new TestNewerModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1), 100);
+ ModeledFramework<TestNewerModel> clientForNew = ModeledFramework.wrap(async, newModelSpec);
+ complete(clientForNew.set(rawNewModel), (s, e) -> Assert.assertNotNull(s));
+
+ ModeledFramework<TestModel> clientForOld = ModeledFramework.wrap(async, modelSpec);
+ complete(clientForOld.read(), (model, e) -> Assert.assertTrue(rawNewModel.equalsOld(model)));
+ }
+
+ @Test
+ public void testWatched() throws InterruptedException
+ {
+ CountDownLatch latch = new CountDownLatch(1);
+ ModeledFramework<TestModel> client = ModeledFramework.builder(async, modelSpec).watched().build();
+ client.checkExists().event().whenComplete((event, ex) -> latch.countDown());
+ timing.sleepABit();
+ Assert.assertEquals(latch.getCount(), 1);
+ client.set(new TestModel());
+ Assert.assertTrue(timing.awaitLatch(latch));
+ }
+
+ @Test
+ public void testGetChildren()
+ {
+ TestModel model = new TestModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1));
+ ModeledFramework<TestModel> client = ModeledFramework.builder(async, modelSpec).build();
+ complete(client.child("one").set(model));
+ complete(client.child("two").set(model));
+ complete(client.child("three").set(model));
+
+ Set<ZPath> expected = Sets.newHashSet(path.child("one"), path.child("two"), path.child("three"));
+ complete(client.children(), (children, e) -> Assert.assertEquals(Sets.newHashSet(children), expected));
+ }
+
+ @Test
+ public void testBadNode()
+ {
+ complete(async.create().forPath(modelSpec.path().fullPath(), "fubar".getBytes()));
+
+ ModeledFramework<TestModel> client = ModeledFramework.builder(async, modelSpec).watched().build();
+ complete(client.read().whenComplete((model, e) -> Assert.assertTrue(e instanceof RuntimeException)));
+ }
+
+ @Test
+ public void testSchema() throws Exception
+ {
+ Schema schema = modelSpec.schema();
+ try ( CuratorFramework schemaClient = CuratorFrameworkFactory.builder()
+ .connectString(server.getConnectString())
+ .retryPolicy(new RetryOneTime(1))
+ .schemaSet(new SchemaSet(Collections.singletonList(schema), false))
+ .build() ) {
+ schemaClient.start();
+
+ try
+ {
+ schemaClient.create().forPath(modelSpec.path().fullPath(), "asflasfas".getBytes());
+ Assert.fail("Should've thrown SchemaViolation");
+ }
+ catch ( SchemaViolation dummy )
+ {
+ // expected
+ }
+
+ ModeledFramework<TestModel> modeledSchemaClient = ModeledFramework.wrap(AsyncCuratorFramework.wrap(schemaClient), modelSpec);
+ complete(modeledSchemaClient.set(new TestModel("one", "two", "three", 4, BigInteger.ONE)), (dummy, e) -> Assert.assertNull(e));
+ }
+ }
+
+ @Test
+ public void testVersioned()
+ {
+ ModeledFramework<TestModel> client = ModeledFramework.wrap(async, modelSpec);
+ client.set(new TestModel("John", "Galt", "Galt's Gulch", 21, BigInteger.valueOf(1010101)));
+
+ VersionedModeledFramework<TestModel> versioned = client.versioned();
+ complete(versioned.read().whenComplete((v, e) -> {
+ Assert.assertNull(e);
+ Assert.assertTrue(v.version() > 0);
+ }).thenCompose(versioned::set).whenComplete((s, e) -> Assert.assertNull(e))); // version is correct should succeed
+
+ complete(versioned.read().whenComplete((v, e) -> {
+ Assert.assertNull(e);
+ Assert.assertTrue(v.version() > 0);
+ }).thenCompose(value -> {
+ Versioned<TestModel> badVersion = Versioned.from(value.model(), Integer.MAX_VALUE);
+ return versioned.set(badVersion);
+ }).whenComplete((s, e) -> Assert.assertTrue(e instanceof KeeperException.BadVersionException)));
+ }
+
+ @Test
+ public void testAcl() throws NoSuchAlgorithmException
+ {
+ List<ACL> aclList = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("digest", DigestAuthenticationProvider.generateDigest("test:test"))));
+ ModelSpec<TestModel> aclModelSpec = ModelSpec.builder(modelSpec.path(), modelSpec.serializer()).withAclList(aclList).build();
+ ModeledFramework<TestModel> client = ModeledFramework.wrap(async, aclModelSpec);
+ complete(client.set(new TestModel("John", "Galt", "Galt's Gulch", 21, BigInteger.valueOf(1010101))));
+ complete(client.update(new TestModel("John", "Galt", "Galt's Gulch", 54, BigInteger.valueOf(88))), (__, e) -> Assert.assertNotNull(e, "Should've gotten an auth failure"));
+
+ try ( CuratorFramework authCurator = CuratorFrameworkFactory.builder()
+ .connectString(server.getConnectString())
+ .retryPolicy(new RetryOneTime(1))
+ .authorization("digest", "test:test".getBytes())
+ .build() )
+ {
+ authCurator.start();
+ ModeledFramework<TestModel> authClient = ModeledFramework.wrap(AsyncCuratorFramework.wrap(authCurator), aclModelSpec);
+ complete(authClient.update(new TestModel("John", "Galt", "Galt's Gulch", 42, BigInteger.valueOf(66))), (__, e) -> Assert.assertNull(e, "Should've succeeded"));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFrameworkBase.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFrameworkBase.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFrameworkBase.java
new file mode 100644
index 0000000..61a4570
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledFrameworkBase.java
@@ -0,0 +1,64 @@
+/**
+ * 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.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.CompletableBaseClassForTests;
+import org.apache.curator.x.async.modeled.models.TestModel;
+import org.apache.curator.x.async.modeled.models.TestNewerModel;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+public class TestModeledFrameworkBase extends CompletableBaseClassForTests
+{
+ protected static final ZPath path = ZPath.parse("/test/path");
+ protected CuratorFramework rawClient;
+ protected ModelSpec<TestModel> modelSpec;
+ protected ModelSpec<TestNewerModel> newModelSpec;
+ protected AsyncCuratorFramework async;
+
+ @BeforeMethod
+ @Override
+ public void setup() throws Exception
+ {
+ super.setup();
+
+ rawClient = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+ rawClient.start();
+ async = AsyncCuratorFramework.wrap(rawClient);
+
+ JacksonModelSerializer<TestModel> serializer = JacksonModelSerializer.build(TestModel.class);
+ JacksonModelSerializer<TestNewerModel> newSerializer = JacksonModelSerializer.build(TestNewerModel.class);
+
+ modelSpec = ModelSpec.builder(path, serializer).build();
+ newModelSpec = ModelSpec.builder(path, newSerializer).build();
+ }
+
+ @AfterMethod
+ @Override
+ public void teardown() throws Exception
+ {
+ CloseableUtils.closeQuietly(rawClient);
+ super.teardown();
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
new file mode 100644
index 0000000..d2c24da
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
@@ -0,0 +1,126 @@
+/**
+ * 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.utils.ZKPaths;
+import org.apache.curator.x.async.modeled.details.ZPathImpl;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.apache.curator.x.async.modeled.ZPath.parameter;
+
+public class TestZPath
+{
+ @Test
+ public void testRoot()
+ {
+ Assert.assertEquals(ZPath.root.nodeName(), ZKPaths.PATH_SEPARATOR);
+ Assert.assertEquals(ZPath.root, ZPathImpl.root);
+ Assert.assertTrue(ZPath.root.isRoot());
+ Assert.assertEquals(ZPath.root.child("foo").parent(), ZPath.root);
+ Assert.assertTrue(ZPath.root.child("foo").parent().isRoot());
+ }
+
+ @Test
+ public void testBasic()
+ {
+ ZPath path = ZPath.root.child("one").child("two");
+ Assert.assertFalse(path.isRoot());
+ Assert.assertEquals(path, ZPath.root.child("one").child("two"));
+ Assert.assertNotEquals(path, ZPath.root.child("onex").child("two"));
+ Assert.assertEquals(path.nodeName(), "two");
+ Assert.assertEquals(path.fullPath(), "/one/two");
+ Assert.assertEquals(path.parent().fullPath(), "/one");
+ Assert.assertEquals(path.fullPath(), "/one/two"); // call twice to test the internal cache
+ Assert.assertEquals(path.parent().fullPath(), "/one"); // call twice to test the internal cache
+
+ Assert.assertTrue(path.startsWith(ZPath.root.child("one")));
+ Assert.assertFalse(path.startsWith(ZPath.root.child("two")));
+
+ ZPath checkIdLike = ZPath.parse("/one/{two}/three");
+ Assert.assertTrue(checkIdLike.isResolved());
+ checkIdLike = ZPath.parse("/one/" + ZPath.parameter() + "/three");
+ Assert.assertTrue(checkIdLike.isResolved());
+ checkIdLike = ZPath.parse("/one/" + ZPath.parameter("others") + "/three");
+ Assert.assertTrue(checkIdLike.isResolved());
+ }
+
+ @Test
+ public void testParsing()
+ {
+ Assert.assertEquals(ZPath.parse("/"), ZPath.root);
+ Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.root.child("one").child("two").child("three"));
+ Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.from("one", "two", "three"));
+ Assert.assertEquals(ZPath.parseWithIds("/one/{id}/two/{id}"), ZPath.from("one", parameter(), "two", parameter()));
+ }
+
+ @Test(expectedExceptions = IllegalStateException.class)
+ public void testUnresolvedPath()
+ {
+ ZPath path = ZPath.from("one", parameter(), "two");
+ path.fullPath();
+ }
+
+ @Test
+ public void testResolvedPath()
+ {
+ ZPath path = ZPath.from("one", parameter(), "two", parameter());
+ Assert.assertEquals(path.resolved("a", "b"), ZPath.from("one", "a", "two", "b"));
+ }
+
+ @Test
+ public void testSchema()
+ {
+ ZPath path = ZPath.from("one", parameter(), "two", parameter());
+ Assert.assertEquals(path.toSchemaPathPattern().toString(), "/one/.*/two/.*");
+ path = ZPath.parse("/one/two/three");
+ Assert.assertEquals(path.toSchemaPathPattern().toString(), "/one/two/three");
+ path = ZPath.parseWithIds("/one/{id}/three");
+ Assert.assertEquals(path.toSchemaPathPattern().toString(), "/one/.*/three");
+ path = ZPath.parseWithIds("/{id}/{id}/three");
+ Assert.assertEquals(path.toSchemaPathPattern().toString(), "/.*/.*/three");
+ }
+
+ @Test
+ public void testCustomIds()
+ {
+ Assert.assertEquals(ZPath.parseWithIds("/a/{a}/bee/{bee}/c/{c}").toString(), "/a/{a}/bee/{bee}/c/{c}");
+ Assert.assertEquals(ZPath.from("a", parameter(), "b", parameter()).toString(), "/a/{id}/b/{id}");
+ Assert.assertEquals(ZPath.from("a", parameter("foo"), "b", parameter("bar")).toString(), "/a/{foo}/b/{bar}");
+ }
+
+ @Test
+ public void testPartialResolution()
+ {
+ ZPath path = ZPath.parseWithIds("/one/{1}/two/{2}");
+ Assert.assertFalse(path.parent().isResolved());
+ Assert.assertFalse(path.parent().parent().isResolved());
+ Assert.assertTrue(path.parent().parent().parent().isResolved());
+ Assert.assertFalse(path.isResolved());
+
+ path = path.resolved("p1");
+ Assert.assertFalse(path.isResolved());
+ Assert.assertTrue(path.parent().isResolved());
+ Assert.assertEquals(path.toString(), "/one/p1/two/{2}");
+
+ path = path.resolved("p2");
+ Assert.assertTrue(path.isResolved());
+ Assert.assertEquals(path.toString(), "/one/p1/two/p2");
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
new file mode 100644
index 0000000..8a92d33
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
@@ -0,0 +1,115 @@
+/**
+ * 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.models;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class TestModel
+{
+ private final String firstName;
+ private final String lastName;
+ private final String address;
+ private final int age;
+ private final BigInteger salary;
+
+ public TestModel()
+ {
+ this("", "", "", 0, BigInteger.ZERO);
+ }
+
+ public TestModel(String firstName, String lastName, String address, int age, BigInteger salary)
+ {
+ this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null");
+ this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null");
+ this.address = Objects.requireNonNull(address, "address cannot be null");
+ this.age = Objects.requireNonNull(age, "age cannot be null");
+ this.salary = salary;
+ }
+
+ public String getFirstName()
+ {
+ return firstName;
+ }
+
+ public String getLastName()
+ {
+ return lastName;
+ }
+
+ public String getAddress()
+ {
+ return address;
+ }
+
+ public int getAge()
+ {
+ return age;
+ }
+
+ public BigInteger getSalary()
+ {
+ return salary;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ TestModel testModel = (TestModel)o;
+
+ if ( age != testModel.age )
+ {
+ return false;
+ }
+ if ( !firstName.equals(testModel.firstName) )
+ {
+ return false;
+ }
+ if ( !lastName.equals(testModel.lastName) )
+ {
+ return false;
+ }
+ //noinspection SimplifiableIfStatement
+ if ( !address.equals(testModel.address) )
+ {
+ return false;
+ }
+ return salary.equals(testModel.salary);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = firstName.hashCode();
+ result = 31 * result + lastName.hashCode();
+ result = 31 * result + address.hashCode();
+ result = 31 * result + age;
+ result = 31 * result + salary.hashCode();
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
new file mode 100644
index 0000000..94e82fb
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
@@ -0,0 +1,137 @@
+/**
+ * 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.models;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class TestNewerModel
+{
+ private final String firstName;
+ private final String lastName;
+ private final String address;
+ private final int age;
+ private final BigInteger salary;
+ private final long newField;
+
+ public TestNewerModel()
+ {
+ this("", "", "", 0, BigInteger.ZERO, 0);
+ }
+
+ public TestNewerModel(String firstName, String lastName, String address, int age, BigInteger salary, long newField)
+ {
+ this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null");
+ this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null");
+ this.address = Objects.requireNonNull(address, "address cannot be null");
+ this.age = Objects.requireNonNull(age, "age cannot be null");
+ this.salary = salary;
+ this.newField = newField;
+ }
+
+ public String getFirstName()
+ {
+ return firstName;
+ }
+
+ public String getLastName()
+ {
+ return lastName;
+ }
+
+ public String getAddress()
+ {
+ return address;
+ }
+
+ public int getAge()
+ {
+ return age;
+ }
+
+ public BigInteger getSalary()
+ {
+ return salary;
+ }
+
+ public long getNewField()
+ {
+ return newField;
+ }
+
+ public boolean equalsOld(TestModel model)
+ {
+ return firstName.equals(model.getFirstName())
+ && lastName.equals(model.getLastName())
+ && address.equals(model.getAddress())
+ && salary.equals(model.getSalary())
+ && age == model.getAge()
+ ;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ TestNewerModel that = (TestNewerModel)o;
+
+ if ( age != that.age )
+ {
+ return false;
+ }
+ if ( newField != that.newField )
+ {
+ return false;
+ }
+ if ( !firstName.equals(that.firstName) )
+ {
+ return false;
+ }
+ if ( !lastName.equals(that.lastName) )
+ {
+ return false;
+ }
+ //noinspection SimplifiableIfStatement
+ if ( !address.equals(that.address) )
+ {
+ return false;
+ }
+ return salary.equals(that.salary);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = firstName.hashCode();
+ result = 31 * result + lastName.hashCode();
+ result = 31 * result + address.hashCode();
+ result = 31 * result + age;
+ result = 31 * result + salary.hashCode();
+ result = 31 * result + (int)(newField ^ (newField >>> 32));
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestSimpleModel.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestSimpleModel.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestSimpleModel.java
new file mode 100644
index 0000000..f998da2
--- /dev/null
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestSimpleModel.java
@@ -0,0 +1,84 @@
+/**
+ * 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.models;
+
+import java.util.Objects;
+
+public class TestSimpleModel
+{
+ private final String name;
+ private final int age;
+
+ public TestSimpleModel()
+ {
+ this("", 0);
+ }
+
+ public TestSimpleModel(String name, int age)
+ {
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.age = Objects.requireNonNull(age, "age cannot be null");
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public int getAge()
+ {
+ return age;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ TestSimpleModel that = (TestSimpleModel)o;
+
+ //noinspection SimplifiableIfStatement
+ if ( age != that.age )
+ {
+ return false;
+ }
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = name.hashCode();
+ result = 31 * result + age;
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "TestSimpleModel{" + "name='" + name + '\'' + ", age=" + age + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-x-async/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/resources/log4j.properties b/curator-x-async/src/test/resources/log4j.properties
new file mode 100644
index 0000000..2a85e0d
--- /dev/null
+++ b/curator-x-async/src/test/resources/log4j.properties
@@ -0,0 +1,27 @@
+#
+# 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.logger.org.apache.curator=DEBUG, console
+log4j.additivity.org.apache.curator=false
+
+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