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/05/02 17:59:12 UTC

[1/3] curator git commit: fixed startsWith

Repository: curator
Updated Branches:
  refs/heads/CURATOR-397 c3db1810a -> ef9df2b79


fixed startsWith


Project: http://git-wip-us.apache.org/repos/asf/curator/repo
Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/56c6c85a
Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/56c6c85a
Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/56c6c85a

Branch: refs/heads/CURATOR-397
Commit: 56c6c85a2f26ef914574139e4c1c657b5a7573a5
Parents: c3db181
Author: randgalt <ra...@apache.org>
Authored: Tue May 2 01:11:13 2017 -0500
Committer: randgalt <ra...@apache.org>
Committed: Tue May 2 01:11:13 2017 -0500

----------------------------------------------------------------------
 .../curator/x/async/modeled/details/ZPathImpl.java   | 15 ++++++++++++---
 .../apache/curator/x/async/modeled/TestZPath.java    | 12 +++++++++---
 2 files changed, 21 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/curator/blob/56c6c85a/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
index f6c941a..b6c0d2f 100644
--- 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
@@ -47,6 +47,11 @@ public class ZPathImpl implements ZPath
 
     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(ZKPaths.PATH_SEPARATOR)
             .addAll(
@@ -123,12 +128,16 @@ public class ZPathImpl implements ZPath
     @Override
     public boolean startsWith(ZPath path)
     {
+        ZPathImpl rhs;
         if ( path instanceof ZPathImpl )
         {
-            ZPathImpl rhs = (ZPathImpl)path;
-            return (nodes.size() >= rhs.nodes.size()) && nodes.subList(0, rhs.nodes.size()).equals(rhs);
+            rhs = (ZPathImpl)path;
+        }
+        else
+        {
+            rhs = parseInternal(path.fullPath(), s -> s);
         }
-        return false;
+        return (nodes.size() >= rhs.nodes.size()) && nodes.subList(0, rhs.nodes.size()).equals(rhs.nodes);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/curator/blob/56c6c85a/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
index 8397ef5..7d8a463 100644
--- 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
@@ -25,6 +25,8 @@ import org.testng.annotations.Test;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.apache.curator.x.async.modeled.ZPath.parameterNodeName;
+
 public class TestZPath
 {
     @Test
@@ -47,6 +49,9 @@ public class TestZPath
         Assert.assertEquals(path.nodeName(), "two");
         Assert.assertEquals(path.fullPath(), "/one/two");
         Assert.assertEquals(path.parentPath(), "/one");
+
+        Assert.assertTrue(path.startsWith(ZPath.root().at("one")));
+        Assert.assertFalse(path.startsWith(ZPath.root().at("two")));
     }
 
     @Test
@@ -55,26 +60,27 @@ public class TestZPath
         Assert.assertEquals(ZPath.parse("/"), ZPath.root());
         Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.root().at("one").at("two").at("three"));
         Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.from("one", "two", "three"));
+        Assert.assertEquals(ZPath.parseWithIds("/one/{id}/two/{id}"), ZPath.from("one", parameterNodeName(), "two", parameterNodeName()));
     }
 
     @Test(expectedExceptions = IllegalStateException.class)
     public void testUnresolvedPath()
     {
-        ZPath path = ZPath.from("one", ZPath.parameterNodeName(), "two");
+        ZPath path = ZPath.from("one", parameterNodeName(), "two");
         path.fullPath();
     }
 
     @Test
     public void testResolvedPath()
     {
-        ZPath path = ZPath.from("one", ZPath.parameterNodeName(), "two", ZPath.parameterNodeName());
+        ZPath path = ZPath.from("one", parameterNodeName(), "two", parameterNodeName());
         Assert.assertEquals(path.resolved("a", "b"), ZPath.from("one", "a", "two", "b"));
     }
 
     @Test
     public void testResolving()
     {
-        ZPath path = ZPath.from("one", ZPath.parameterNodeName(), "two", ZPath.parameterNodeName());
+        ZPath path = ZPath.from("one", parameterNodeName(), "two", parameterNodeName());
         AtomicInteger count = new AtomicInteger(0);
         ZPath resolving = path.resolving(Arrays.asList(() -> "x" + count.get(), () -> "y" + count.get()));
         Assert.assertEquals(resolving.fullPath(), "/one/x0/two/y0");


[2/3] curator git commit: wip

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index b6c0d2f..a184f30 100644
--- 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
@@ -35,11 +35,13 @@ import java.util.function.UnaryOperator;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static org.apache.curator.utils.ZKPaths.PATH_SEPARATOR;
+import static org.apache.curator.x.async.modeled.ZPath.idName;
+import static org.apache.curator.x.async.modeled.ZPath.parameterNodeName;
+
 public class ZPathImpl implements ZPath
 {
-    public static final ZPath root = new ZPathImpl(Collections.singletonList(ZKPaths.PATH_SEPARATOR), null, null);
-
-    public static final String parameter = "";    // empty paths are illegal so it's useful for this purpose
+    public static final ZPath root = new ZPathImpl(Collections.singletonList(PATH_SEPARATOR), null, null);
 
     private final List<String> nodes;
     private final boolean isResolved;
@@ -53,9 +55,9 @@ public class ZPathImpl implements ZPath
     private static ZPathImpl parseInternal(String fullPath, UnaryOperator<String> nameFilter)
     {
         List<String> nodes = ImmutableList.<String>builder()
-            .add(ZKPaths.PATH_SEPARATOR)
+            .add(PATH_SEPARATOR)
             .addAll(
-                Splitter.on(ZKPaths.PATH_SEPARATOR)
+                Splitter.on(PATH_SEPARATOR)
                     .omitEmptyStrings()
                     .splitToList(fullPath)
                     .stream()
@@ -95,12 +97,12 @@ public class ZPathImpl implements ZPath
             }
             else
             {
-                builder.addAll(Splitter.on(ZKPaths.PATH_SEPARATOR).omitEmptyStrings().splitToList(base.fullPath()));
+                builder.addAll(Splitter.on(PATH_SEPARATOR).omitEmptyStrings().splitToList(base.fullPath()));
             }
         }
         else
         {
-            builder.add(ZKPaths.PATH_SEPARATOR);
+            builder.add(PATH_SEPARATOR);
         }
         List<String> nodes = builder.addAll(names).build();
         return new ZPathImpl(nodes, null, null);
@@ -143,7 +145,7 @@ public class ZPathImpl implements ZPath
     @Override
     public Pattern toSchemaPathPattern()
     {
-        return Pattern.compile(fullPath() + ZKPaths.PATH_SEPARATOR + ".*");
+        return Pattern.compile(fullPath() + PATH_SEPARATOR + ".*");
     }
 
     @Override
@@ -193,7 +195,7 @@ public class ZPathImpl implements ZPath
     @Override
     public String toString()
     {
-        String value = nodes.stream().map(name -> name.equals(parameter) ? "{p}" : name).collect(Collectors.joining(ZKPaths.PATH_SEPARATOR, ZKPaths.PATH_SEPARATOR, ""));
+        String value = nodes.stream().map(name -> name.equals(parameterNodeName) ? idName : name).collect(Collectors.joining(PATH_SEPARATOR, PATH_SEPARATOR, ""));
         return "ZPathImpl{" + value + '}';
     }
 
@@ -203,7 +205,7 @@ public class ZPathImpl implements ZPath
         Iterator<Object> iterator = parameters.iterator();
         List<String> nodeNames = nodes.stream()
             .map(name -> {
-                if ( name.equals(parameter) )
+                if ( name.equals(parameterNodeName) )
                 {
                     if ( !iterator.hasNext() )
                     {
@@ -234,7 +236,7 @@ public class ZPathImpl implements ZPath
             builder.add(child);
         }
         this.nodes = builder.build();
-        isResolved = (parameterSuppliers != null) || !this.nodes.contains(parameter);
+        isResolved = (parameterSuppliers != null) || !this.nodes.contains(parameterNodeName);
     }
 
     private void checkRootAccess()
@@ -260,10 +262,10 @@ public class ZPathImpl implements ZPath
         {
             if ( i > 1 )
             {
-                str.append(ZKPaths.PATH_SEPARATOR);
+                str.append(PATH_SEPARATOR);
             }
             String value = nodes.get(i);
-            if ( value.equals(parameter) )
+            if ( value.equals(parameterNodeName) )
             {
                 if ( (parameterSuppliers == null) || (parameterSuppliers.size() <= parameterIndex) )
                 {
@@ -278,14 +280,14 @@ public class ZPathImpl implements ZPath
 
     private static void validate(String nodeName)
     {
-        if ( parameter.equals(Objects.requireNonNull(nodeName, "nodeName cannot be null")) )
+        if ( parameterNodeName.equals(Objects.requireNonNull(nodeName, "nodeName cannot be null")) )
         {
             return;
         }
-        if ( nodeName.equals(ZKPaths.PATH_SEPARATOR) )
+        if ( nodeName.equals(PATH_SEPARATOR) )
         {
             return;
         }
-        PathUtils.validatePath(ZKPaths.PATH_SEPARATOR + nodeName);
+        PathUtils.validatePath(PATH_SEPARATOR + nodeName);
     }
 }

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..8e0f101
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec.java
@@ -0,0 +1,29 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..b31aaa5
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec10.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..19fed0a
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec2.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..2dc7595
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec3.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..d233be5
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec4.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..685cf2c
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec5.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..abe811f
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec6.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..a4e6fdd
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec7.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..52b2cb4
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec8.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..a1ab075
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/TypedModelSpec9.java
@@ -0,0 +1,23 @@
+package org.apache.curator.x.async.modeled.typed;
+
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+
+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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledCuratorFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledCuratorFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledCuratorFramework.java
index 40e30f0..d54ef6f 100644
--- a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledCuratorFramework.java
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledCuratorFramework.java
@@ -41,8 +41,8 @@ public class TestModeledCuratorFramework extends CompletableBaseClassForTests
     private CuratorFramework rawClient;
     private JacksonModelSerializer<TestModel> serializer;
     private JacksonModelSerializer<TestNewerModel> newSerializer;
-    private CuratorModelSpec<TestModel> modelSpec;
-    private CuratorModelSpec<TestNewerModel> newModelSpec;
+    private ModelSpec<TestModel> modelSpec;
+    private ModelSpec<TestNewerModel> newModelSpec;
 
     @BeforeMethod
     @Override
@@ -56,8 +56,8 @@ public class TestModeledCuratorFramework extends CompletableBaseClassForTests
         serializer = new JacksonModelSerializer<>(TestModel.class);
         newSerializer = new JacksonModelSerializer<>(TestNewerModel.class);
 
-        modelSpec = CuratorModelSpec.builder(path, serializer).build();
-        newModelSpec = CuratorModelSpec.builder(path, newSerializer).build();
+        modelSpec = ModelSpec.builder(path, serializer).build();
+        newModelSpec = ModelSpec.builder(path, newSerializer).build();
     }
 
     @AfterMethod

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index 7d8a463..4df9ed1 100644
--- 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
@@ -60,27 +60,27 @@ public class TestZPath
         Assert.assertEquals(ZPath.parse("/"), ZPath.root());
         Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.root().at("one").at("two").at("three"));
         Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.from("one", "two", "three"));
-        Assert.assertEquals(ZPath.parseWithIds("/one/{id}/two/{id}"), ZPath.from("one", parameterNodeName(), "two", parameterNodeName()));
+        Assert.assertEquals(ZPath.parseWithIds("/one/{id}/two/{id}"), ZPath.from("one", parameterNodeName, "two", parameterNodeName));
     }
 
     @Test(expectedExceptions = IllegalStateException.class)
     public void testUnresolvedPath()
     {
-        ZPath path = ZPath.from("one", parameterNodeName(), "two");
+        ZPath path = ZPath.from("one", parameterNodeName, "two");
         path.fullPath();
     }
 
     @Test
     public void testResolvedPath()
     {
-        ZPath path = ZPath.from("one", parameterNodeName(), "two", parameterNodeName());
+        ZPath path = ZPath.from("one", parameterNodeName, "two", parameterNodeName);
         Assert.assertEquals(path.resolved("a", "b"), ZPath.from("one", "a", "two", "b"));
     }
 
     @Test
     public void testResolving()
     {
-        ZPath path = ZPath.from("one", parameterNodeName(), "two", parameterNodeName());
+        ZPath path = ZPath.from("one", parameterNodeName, "two", parameterNodeName);
         AtomicInteger count = new AtomicInteger(0);
         ZPath resolving = path.resolving(Arrays.asList(() -> "x" + count.get(), () -> "y" + count.get()));
         Assert.assertEquals(resolving.fullPath(), "/one/x0/two/y0");

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledCuratorFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledCuratorFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledCuratorFramework.java
index e4f0158..1bc3434 100644
--- a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledCuratorFramework.java
+++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledCuratorFramework.java
@@ -24,7 +24,7 @@ import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.x.async.CompletableBaseClassForTests;
 import org.apache.curator.x.async.modeled.cached.CachedModeledCuratorFramework;
-import org.apache.curator.x.async.modeled.CuratorModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpec;
 import org.apache.curator.x.async.modeled.JacksonModelSerializer;
 import org.apache.curator.x.async.modeled.ModelSerializer;
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
@@ -52,7 +52,7 @@ public class TestCachedModeledCuratorFramework extends CompletableBaseClassForTe
         rawClient.start();
 
         ModelSerializer<TestSimpleModel> serializer = new JacksonModelSerializer<>(TestSimpleModel.class);
-        client = ModeledCuratorFramework.builder(rawClient, CuratorModelSpec.builder(path, serializer).build()).build().cached();
+        client = ModeledCuratorFramework.builder(rawClient, ModelSpec.builder(path, serializer).build()).build().cached();
     }
 
     @AfterMethod


[3/3] curator git commit: wip

Posted by ra...@apache.org.
wip


Project: http://git-wip-us.apache.org/repos/asf/curator/repo
Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/ef9df2b7
Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/ef9df2b7
Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/ef9df2b7

Branch: refs/heads/CURATOR-397
Commit: ef9df2b7915ced99e227e2c2cd6f9ec12c7d7309
Parents: 56c6c85
Author: randgalt <ra...@apache.org>
Authored: Tue May 2 12:58:50 2017 -0500
Committer: randgalt <ra...@apache.org>
Committed: Tue May 2 12:58:50 2017 -0500

----------------------------------------------------------------------
 .../java/modeled/ModeledCuratorExamples.java    |   4 +-
 .../src/main/java/modeled/PersonModelSpec.java  |   8 +-
 .../src/main/java/modeledRegistry/Clients.java  |  35 +++
 .../main/java/modeledRegistry/ModelSpecs.java   |  37 ++++
 .../src/main/java/modeledRegistry/Paths.java    |  17 ++
 .../java/modeledRegistry/RegistryExamples.java  |  20 ++
 .../main/java/modeledRegistry/models/Cache.java |  24 ++
 .../java/modeledRegistry/models/Database.java   |  32 +++
 .../java/modeledRegistry/models/InstanceId.java |  24 ++
 .../java/modeledRegistry/models/Server.java     |  25 +++
 .../java/modeledRegistry/models/Session.java    |  24 ++
 .../main/java/modeledRegistry/models/Zone.java  |  36 +++
 .../x/async/modeled/CuratorModelSpec.java       | 118 ----------
 .../async/modeled/CuratorModelSpecBuilder.java  | 104 ---------
 .../curator/x/async/modeled/ModelSpec.java      | 143 ++++++++++++
 .../x/async/modeled/ModelSpecBuilder.java       | 136 ++++++++++++
 .../async/modeled/ModeledCuratorFramework.java  |  35 +--
 .../modeled/ModeledCuratorFrameworkBuilder.java |   4 +-
 .../apache/curator/x/async/modeled/ZPath.java   |  31 ++-
 .../cached/CachedModeledCuratorFramework.java   |  14 ++
 .../x/async/modeled/cached/ModeledCache.java    |   4 +-
 .../async/modeled/cached/ModeledCachedNode.java |  49 -----
 .../curator/x/async/modeled/cached/ZNode.java   |  49 +++++
 .../CachedModeledCuratorFrameworkImpl.java      |  39 ++--
 .../modeled/details/CuratorModelSpecImpl.java   | 208 ------------------
 .../x/async/modeled/details/ModelSpecImpl.java  | 217 +++++++++++++++++++
 .../async/modeled/details/ModeledCacheImpl.java |  40 +---
 .../details/ModeledCuratorFrameworkImpl.java    |  62 +++---
 .../x/async/modeled/details/ZNodeImpl.java      |  38 ++++
 .../x/async/modeled/details/ZPathImpl.java      |  34 +--
 .../x/async/modeled/typed/TypedModelSpec.java   |  29 +++
 .../x/async/modeled/typed/TypedModelSpec10.java |  23 ++
 .../x/async/modeled/typed/TypedModelSpec2.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec3.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec4.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec5.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec6.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec7.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec8.java  |  23 ++
 .../x/async/modeled/typed/TypedModelSpec9.java  |  23 ++
 .../modeled/TestModeledCuratorFramework.java    |   8 +-
 .../curator/x/async/modeled/TestZPath.java      |   8 +-
 .../TestCachedModeledCuratorFramework.java      |   4 +-
 43 files changed, 1241 insertions(+), 626 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index aa37ab1..3e0d140 100644
--- a/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
+++ b/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
@@ -19,7 +19,7 @@
 package modeled;
 
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.x.async.modeled.CuratorModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpec;
 import org.apache.curator.x.async.modeled.JacksonModelSerializer;
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
 import org.apache.curator.x.async.modeled.ZPath;
@@ -32,7 +32,7 @@ public class ModeledCuratorExamples
         JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
 
         // build a model specification - you can pre-build all the model specifications for your app at startup
-        CuratorModelSpec<PersonModel> modelSpec = CuratorModelSpec.builder(ZPath.parse("/example/path"), serializer).build();
+        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 ModeledCuratorFramework instance.

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index 3f0198d..7e0d821 100644
--- a/curator-examples/src/main/java/modeled/PersonModelSpec.java
+++ b/curator-examples/src/main/java/modeled/PersonModelSpec.java
@@ -19,7 +19,7 @@
 package modeled;
 
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.x.async.modeled.CuratorModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpec;
 import org.apache.curator.x.async.modeled.JacksonModelSerializer;
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
 import org.apache.curator.x.async.modeled.ZPath;
@@ -27,7 +27,7 @@ import org.apache.curator.x.async.modeled.ZPath;
 public class PersonModelSpec
 {
     private final CuratorFramework client;
-    private final CuratorModelSpec<PersonModel> modelSpec;
+    private final ModelSpec<PersonModel> modelSpec;
 
     public PersonModelSpec(CuratorFramework client)
     {
@@ -35,12 +35,12 @@ public class PersonModelSpec
 
         JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
         ZPath path = ZPath.from("example", ZPath.parameterNodeName(), "path", ZPath.parameterNodeName());
-        modelSpec = CuratorModelSpec.builder(path, serializer).build();
+        modelSpec = ModelSpec.builder(path, serializer).build();
     }
 
     public ModeledCuratorFramework<PersonModel> resolved(ContainerType containerType, PersonId personId)
     {
-        CuratorModelSpec<PersonModel> resolved = modelSpec.resolved(containerType.getTypeId(), personId.getId());
+        ModelSpec<PersonModel> resolved = null;// TODO modelSpec.resolved(containerType.getTypeId(), personId.getId());
         return ModeledCuratorFramework.wrap(client, resolved);
     }
 }

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/Clients.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/Clients.java b/curator-examples/src/main/java/modeledRegistry/Clients.java
new file mode 100644
index 0000000..e9071e7
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/Clients.java
@@ -0,0 +1,35 @@
+package modeledRegistry;
+
+import modeledRegistry.models.Cache;
+import modeledRegistry.models.Database;
+import modeledRegistry.models.InstanceId;
+import modeledRegistry.models.Session;
+import modeledRegistry.models.Zone;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
+
+import static modeledRegistry.ModelSpecs.cacheModelSpec;
+import static modeledRegistry.ModelSpecs.databaseModelSpec;
+import static modeledRegistry.ModelSpecs.sessionModelSpec;
+
+public class Clients
+{
+    public static ModeledCuratorFramework<Cache> cacheClient(CuratorFramework client, Zone zone, InstanceId id)
+    {
+        return ModeledCuratorFramework.wrap(client, cacheModelSpec.resolved(zone, id));
+    }
+
+    public static ModeledCuratorFramework<Session> sessionClient(CuratorFramework client, Zone zone, InstanceId id)
+    {
+        return ModeledCuratorFramework.wrap(client, sessionModelSpec.resolved(zone, id));
+    }
+
+    public static ModeledCuratorFramework<Database> databaseClient(CuratorFramework client, Zone zone)
+    {
+        return ModeledCuratorFramework.wrap(client, databaseModelSpec.resolved(zone));
+    }
+
+    private Clients()
+    {
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/ModelSpecs.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/ModelSpecs.java b/curator-examples/src/main/java/modeledRegistry/ModelSpecs.java
new file mode 100644
index 0000000..96469cc
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/ModelSpecs.java
@@ -0,0 +1,37 @@
+package modeledRegistry;
+
+import modeledRegistry.models.Cache;
+import modeledRegistry.models.Database;
+import modeledRegistry.models.InstanceId;
+import modeledRegistry.models.Session;
+import modeledRegistry.models.Zone;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.typed.TypedModelSpec;
+import org.apache.curator.x.async.modeled.typed.TypedModelSpec2;
+
+import static modeledRegistry.Paths.cachePath;
+import static modeledRegistry.Paths.databasePath;
+import static modeledRegistry.Paths.sessionPath;
+
+public class ModelSpecs
+{
+    public static final TypedModelSpec2<Cache, Zone, InstanceId> cacheModelSpec = TypedModelSpec2.from(
+        ModelSpec.builder(JacksonModelSerializer.build(Cache.class)).withNodeName(Cache::getId),
+        cachePath
+    );
+
+    public static final TypedModelSpec2<Session, Zone, InstanceId> sessionModelSpec = TypedModelSpec2.from(
+        ModelSpec.builder(JacksonModelSerializer.build(Session.class)).withNodeName(Session::getId),
+        sessionPath
+    );
+
+    public static final TypedModelSpec<Database, Zone> databaseModelSpec = TypedModelSpec.from(
+        ModelSpec.builder(JacksonModelSerializer.build(Database.class)).withNodeName(Database::getServerName),
+        databasePath
+    );
+
+    private ModelSpecs()
+    {
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/Paths.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/Paths.java b/curator-examples/src/main/java/modeledRegistry/Paths.java
new file mode 100644
index 0000000..de23aa4
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/Paths.java
@@ -0,0 +1,17 @@
+package modeledRegistry;
+
+import modeledRegistry.models.InstanceId;
+import modeledRegistry.models.Zone;
+import org.apache.curator.x.async.modeled.typed.TypedZPath;
+import org.apache.curator.x.async.modeled.typed.TypedZPath2;
+
+public class Paths
+{
+    public static final TypedZPath2<Zone, InstanceId> cachePath = TypedZPath2.from("/root/registry/{id}/{id}/caches");
+    public static final TypedZPath2<Zone, InstanceId> sessionPath = TypedZPath2.from("/root/registry/{id}/{id}/sessions");
+    public static final TypedZPath<Zone> databasePath = TypedZPath.from("/root/registry/{id}/dbs");
+
+    private Paths()
+    {
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/RegistryExamples.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/RegistryExamples.java b/curator-examples/src/main/java/modeledRegistry/RegistryExamples.java
new file mode 100644
index 0000000..c344dd9
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/RegistryExamples.java
@@ -0,0 +1,20 @@
+package modeledRegistry;
+
+import modeledRegistry.models.Database;
+import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class RegistryExamples
+{
+    public static void addDatabase(ModeledCuratorFramework<Database> client, Database database)
+    {
+        ModeledCuratorFramework<Database> resolved = client.resolved(database);
+        resolved.set(database);
+    }
+
+    public static void getDatabases(ModeledCuratorFramework<Database> client, Consumer<List<Database>> consumer)
+    {
+//        client.children().thenCombine();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/Cache.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/Cache.java b/curator-examples/src/main/java/modeledRegistry/models/Cache.java
new file mode 100644
index 0000000..a148a5f
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/Cache.java
@@ -0,0 +1,24 @@
+package modeledRegistry.models;
+
+import java.util.UUID;
+
+public class Cache extends Server
+{
+    private final int maxObjects;
+
+    public Cache()
+    {
+        this(UUID.randomUUID().toString(), "localhost", Integer.MAX_VALUE);
+    }
+
+    public Cache(String id, String ipAddress, int maxObjects)
+    {
+        super(id, ipAddress);
+        this.maxObjects = maxObjects;
+    }
+
+    public int getMaxObjects()
+    {
+        return maxObjects;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/Database.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/Database.java b/curator-examples/src/main/java/modeledRegistry/models/Database.java
new file mode 100644
index 0000000..681ed34
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/Database.java
@@ -0,0 +1,32 @@
+package modeledRegistry.models;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class Database extends Server
+{
+    private final String connectionString;
+    private final String serverName;
+
+    public Database()
+    {
+        this(UUID.randomUUID().toString(), "localhost", "", "");
+    }
+
+    public Database(String id, String ipAddress, String serverName, String connectionString)
+    {
+        super(id, ipAddress);
+        this.serverName = Objects.requireNonNull(serverName, "serverName cannot be null");
+        this.connectionString = Objects.requireNonNull(connectionString, "connectionString cannot be null");
+    }
+
+    public String getConnectionString()
+    {
+        return connectionString;
+    }
+
+    public String getServerName()
+    {
+        return serverName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/InstanceId.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/InstanceId.java b/curator-examples/src/main/java/modeledRegistry/models/InstanceId.java
new file mode 100644
index 0000000..24ab98e
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/InstanceId.java
@@ -0,0 +1,24 @@
+package modeledRegistry.models;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class InstanceId
+{
+    private final String id;
+
+    public InstanceId()
+    {
+        this(UUID.randomUUID().toString());
+    }
+
+    public InstanceId(String id)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/Server.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/Server.java b/curator-examples/src/main/java/modeledRegistry/models/Server.java
new file mode 100644
index 0000000..9085b0f
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/Server.java
@@ -0,0 +1,25 @@
+package modeledRegistry.models;
+
+import java.util.Objects;
+
+public abstract class Server
+{
+    private final String id;
+    private final String ipAddress;
+
+    protected Server(String id, String ipAddress)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+        this.ipAddress = Objects.requireNonNull(ipAddress, "ipAddress cannot be null");
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public String getIpAddress()
+    {
+        return ipAddress;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/Session.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/Session.java b/curator-examples/src/main/java/modeledRegistry/models/Session.java
new file mode 100644
index 0000000..230dec2
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/Session.java
@@ -0,0 +1,24 @@
+package modeledRegistry.models;
+
+import java.util.UUID;
+
+public class Session extends Server
+{
+    private final long ttl;
+
+    public Session()
+    {
+        this(UUID.randomUUID().toString(), "localhost", Long.MAX_VALUE);
+    }
+
+    public Session(String id, String ipAddress, long ttl)
+    {
+        super(id, ipAddress);
+        this.ttl = ttl;
+    }
+
+    public long getTtl()
+    {
+        return ttl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-examples/src/main/java/modeledRegistry/models/Zone.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeledRegistry/models/Zone.java b/curator-examples/src/main/java/modeledRegistry/models/Zone.java
new file mode 100644
index 0000000..c0f094e
--- /dev/null
+++ b/curator-examples/src/main/java/modeledRegistry/models/Zone.java
@@ -0,0 +1,36 @@
+package modeledRegistry.models;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class Zone
+{
+    private final String id;
+    private final String name;
+
+    public Zone()
+    {
+        this(UUID.randomUUID().toString(), "");
+    }
+
+    public Zone(String name)
+    {
+        this(UUID.randomUUID().toString(), name);
+    }
+
+    public Zone(String id, String name)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+        this.name = Objects.requireNonNull(name, "name cannot be null");
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java
deleted file mode 100644
index ee1865b..0000000
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * 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;
-
-public interface CuratorModelSpec<T>
-{
-    Set<CreateOption> defaultCreateOptions = ImmutableSet.of(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists);
-    Set<DeleteOption> defaultDeleteOptions = ImmutableSet.of(DeleteOption.guaranteed);
-
-    /**
-     * Start a new CuratorModelBuilder for the given path and serializer. The returned CuratorModelBuilder
-     * 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> CuratorModelSpecBuilder<T> builder(ZPath path, ModelSerializer<T> serializer)
-    {
-        return new CuratorModelSpecBuilder<>(path, serializer)
-            .withCreateOptions(defaultCreateOptions)
-            .withDeleteOptions(defaultDeleteOptions);
-    }
-
-    /**
-     * 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".
-     *
-     * @param child child node.
-     * @return new Modeled Curator instance
-     */
-    CuratorModelSpec<T> at(String child);
-
-    /**
-     * Return a new CuratorModel instance with all the same options but using the given path.
-     *
-     * @param path new path
-     * @return new Modeled Curator instance
-     */
-    CuratorModelSpec<T> at(ZPath path);
-
-    /**
-     * 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 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/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpecBuilder.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpecBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpecBuilder.java
deleted file mode 100644
index 1d91784..0000000
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpecBuilder.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * 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.CuratorModelSpecImpl;
-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 CuratorModelSpecBuilder<T>
-{
-    private final ZPath path;
-    private final ModelSerializer<T> serializer;
-    private CreateMode createMode = CreateMode.PERSISTENT;
-    private List<ACL> aclList = Collections.emptyList();
-    private Set<CreateOption> createOptions = Collections.emptySet();
-    private Set<DeleteOption> deleteOptions = Collections.emptySet();
-
-    /**
-     * Build a new CuratorModel instance
-     *
-     * @return new CuratorModel instance
-     */
-    public CuratorModelSpec<T> build()
-    {
-        return new CuratorModelSpecImpl<>(path, serializer, createMode, aclList, createOptions, deleteOptions);
-    }
-
-    /**
-     * Use the given createMode for create operations on the Modeled Curator's ZNode
-     *
-     * @param createMode create mode
-     * @return this for chaining
-     */
-    public CuratorModelSpecBuilder<T> withCreateMode(CreateMode createMode)
-    {
-        this.createMode = createMode;
-        return this;
-    }
-
-    /**
-     * Use the given aclList for create operations on the Modeled Curator's ZNode
-     *
-     * @param aclList ACLs
-     * @return this for chaining
-     */
-    public CuratorModelSpecBuilder<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 CuratorModelSpecBuilder<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 CuratorModelSpecBuilder<T> withDeleteOptions(Set<DeleteOption> deleteOptions)
-    {
-        this.deleteOptions = (deleteOptions != null) ? ImmutableSet.copyOf(deleteOptions) : null;
-        return this;
-    }
-
-    CuratorModelSpecBuilder(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/ef9df2b7/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..cb0f534
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java
@@ -0,0 +1,143 @@
+/**
+ * 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;
+
+public interface ModelSpec<T>
+{
+    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);
+    }
+
+    /**
+     * 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".
+     *
+     * @param child child node.
+     * @return new Modeled Spec instance
+     */
+    ModelSpec<T> at(String child);
+
+    /**
+     * 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> at(ZPath path);
+
+    /**
+     * Return a new CuratorModel instance with all the same options but using the
+     * {@link ModelSpecBuilder#nodeName} functor
+     * to generate the child node's name
+     *
+     * @param model model to use to generate the name
+     * @return new Modeled Spec instance
+     */
+    ModelSpec<T> resolved(T model);
+
+    /**
+     * 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 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/ef9df2b7/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..16e7696
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpecBuilder.java
@@ -0,0 +1,136 @@
+/**
+ * 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;
+import java.util.function.Function;
+
+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 Function<T, String> nodeName = Object::toString;
+
+    /**
+     * Build a new ModelSpec instance
+     *
+     * @return new ModelSpec instance
+     */
+    public ModelSpec<T> build()
+    {
+        return new ModelSpecImpl<>(path, serializer, createMode, aclList, createOptions, deleteOptions, nodeName);
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Functor that returns the node name to use for a model instance. Default is to call
+     * <code>toString()</code> on the model instance.
+     *
+     * @param nodeName naming functor
+     * @return this for chaining
+     */
+    public ModelSpecBuilder<T> withNodeName(Function<T, String> nodeName)
+    {
+        this.nodeName = Objects.requireNonNull(nodeName, "nodeName cannot be 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/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFramework.java
index a8e8f5c..85d0e30 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFramework.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFramework.java
@@ -25,7 +25,6 @@ import org.apache.curator.x.async.AsyncStage;
 import org.apache.curator.x.async.modeled.cached.CachedModeledCuratorFramework;
 import org.apache.zookeeper.data.Stat;
 import java.util.List;
-import java.util.Map;
 
 public interface ModeledCuratorFramework<T>
 {
@@ -36,7 +35,7 @@ public interface ModeledCuratorFramework<T>
      * @param model the model
      * @return new Modeled Curator instance
      */
-    static <T> ModeledCuratorFramework<T> wrap(CuratorFramework client, CuratorModelSpec<T> model)
+    static <T> ModeledCuratorFramework<T> wrap(CuratorFramework client, ModelSpec<T> model)
     {
         return builder(client, model).build();
     }
@@ -48,7 +47,7 @@ public interface ModeledCuratorFramework<T>
      * @param model the model
      * @return builder
      */
-    static <T> ModeledCuratorFrameworkBuilder<T> builder(CuratorFramework client, CuratorModelSpec<T> model)
+    static <T> ModeledCuratorFrameworkBuilder<T> builder(CuratorFramework client, ModelSpec<T> model)
     {
         return new ModeledCuratorFrameworkBuilder<>(client, model);
     }
@@ -64,7 +63,7 @@ public interface ModeledCuratorFramework<T>
     CachedModeledCuratorFramework<T> cached();
 
     /**
-     * Returns the client that was originally passed to {@link #wrap(org.apache.curator.framework.CuratorFramework, CuratorModelSpec)} or
+     * Returns the client that was originally passed to {@link #wrap(org.apache.curator.framework.CuratorFramework, ModelSpec)} or
      * the builder.
      *
      * @return original client
@@ -76,7 +75,7 @@ public interface ModeledCuratorFramework<T>
      *
      * @return model
      */
-    CuratorModelSpec<T> modelSpec();
+    ModelSpec<T> modelSpec();
 
     /**
      * Return a new Modeled Curator instance with all the same options but applying to the given child node of this Modeled Curator's
@@ -89,6 +88,24 @@ public interface ModeledCuratorFramework<T>
     ModeledCuratorFramework<T> at(String child);
 
     /**
+     * Return a Modeled Curator instance with all the same options but using the given path.
+     *
+     * @param path new path
+     * @return new Modeled Curator instance
+     */
+    ModeledCuratorFramework<T> at(ZPath path);
+
+    /**
+     * Return a new Modeled Curator instance with all the same options but using the
+     * {@link ModelSpecBuilder#nodeName} functor
+     * to generate the child node's name
+     *
+     * @param model model to use to generate the name
+     * @return new Modeled Curator instance
+     */
+    ModeledCuratorFramework<T> resolved(T model);
+
+    /**
      * Create (or update depending on build options) a ZNode at this instance's path with a serialized
      * version of the given model
      *
@@ -181,14 +198,6 @@ public interface ModeledCuratorFramework<T>
     AsyncStage<List<ZPath>> children();
 
     /**
-     * Return the children of this instance's path
-     *
-     * @return AsyncStage
-     * @see org.apache.curator.x.async.AsyncStage
-     */
-    AsyncStage<Map<ZPath, AsyncStage<T>>> readChildren();
-
-    /**
      * 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

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFrameworkBuilder.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFrameworkBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFrameworkBuilder.java
index 76926e9..37224ff 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFrameworkBuilder.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledCuratorFrameworkBuilder.java
@@ -30,7 +30,7 @@ import java.util.function.UnaryOperator;
 public class ModeledCuratorFrameworkBuilder<T>
 {
     private final CuratorFramework client;
-    private final CuratorModelSpec<T> model;
+    private final ModelSpec<T> model;
     private WatchMode watchMode;
     private UnaryOperator<WatchedEvent> watcherFilter;
     private UnhandledErrorListener unhandledErrorListener;
@@ -118,7 +118,7 @@ public class ModeledCuratorFrameworkBuilder<T>
         return this;
     }
 
-    ModeledCuratorFrameworkBuilder(CuratorFramework client, CuratorModelSpec<T> model)
+    ModeledCuratorFrameworkBuilder(CuratorFramework client, ModelSpec<T> model)
     {
         this.client = Objects.requireNonNull(client, "client cannot be null");
         this.model = Objects.requireNonNull(model, "model cannot be null");

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index bb251b1..b50bd01 100644
--- 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
@@ -31,6 +31,14 @@ import java.util.regex.Pattern;
 public interface ZPath
 {
     /**
+     * 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
+     */
+    String parameterNodeName = System.getProperty("curator-zpath-parameter", "");    // empty paths are illegal so it's useful for this purpose
+
+    String idName = System.getProperty("curator-zpath-id-name", "{id}");    // empty paths are illegal so it's useful for this purpose
+
+    /**
      * Return the root path: "/"
      *
      * @return root path
@@ -54,7 +62,7 @@ public interface ZPath
 
     /**
      * Take a ZNode string path and return a ZPath. Each part of the path
-     * that is <code>{id}</code> is replaced with {@link #parameterNodeName()}.
+     * that is <code>{id}</code> is replaced with {@link #parameterNodeName}.
      * E.g. <code>parseWithIds("/one/two/{id}/three/{id}")</code> is the equivalent
      * of calling <code>ZPath.from("one", "two", parameterNodeName(), "three", parameterNodeName())</code>
      *
@@ -64,7 +72,7 @@ public interface ZPath
      */
     static ZPath parseWithIds(String fullPath)
     {
-        return ZPathImpl.parse(fullPath, s -> s.equals("{id}") ? parameterNodeName() : s);
+        return ZPathImpl.parse(fullPath, s -> s.equals(idName) ? parameterNodeName : s);
     }
 
     /**
@@ -134,18 +142,7 @@ public interface ZPath
     }
 
     /**
-     * Return 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
-     *
-     * @return name
-     */
-    static String parameterNodeName()
-    {
-        return ZPathImpl.parameter;
-    }
-
-    /**
-     * When creating paths, any node in the path can be set to {@link #parameterNodeName()}.
+     * When creating paths, any node in the path can be set to {@link #parameterNodeName}.
      * 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
@@ -158,7 +155,7 @@ public interface ZPath
     }
 
     /**
-     * When creating paths, any node in the path can be set to {@link #parameterNodeName()}.
+     * When creating paths, any node in the path can be set to {@link #parameterNodeName}.
      * 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
@@ -169,14 +166,14 @@ public interface ZPath
 
     /**
      * An "auto" resolving version of this ZPath. i.e. if any of the path names is
-     * the {@link #parameterNodeName()} the ZPath must be resolved. This method
+     * the {@link #parameterNodeName} the ZPath must be resolved. This method
      * creates a new ZPath that auto resolves by using the given parameter suppliers
      * whenever needed.
      *
      * @param parameterSuppliers parameter suppliers
      * @return new auto resolving ZNode
      * @see #resolved(Object...)
-     * @see #parameterNodeName()
+     * @see #parameterNodeName
      */
     ZPath resolving(List<Supplier<Object>> parameterSuppliers);
 

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledCuratorFramework.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledCuratorFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledCuratorFramework.java
index 8718e60..9f417dd 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledCuratorFramework.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/CachedModeledCuratorFramework.java
@@ -19,6 +19,7 @@
 package org.apache.curator.x.async.modeled.cached;
 
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
+import org.apache.curator.x.async.modeled.ZPath;
 import java.io.Closeable;
 
 public interface CachedModeledCuratorFramework<T> extends ModeledCuratorFramework<T>, Closeable
@@ -44,5 +45,18 @@ public interface CachedModeledCuratorFramework<T> extends ModeledCuratorFramewor
     /**
      * {@inheritDoc}
      */
+    @Override
     CachedModeledCuratorFramework<T> at(String child);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    CachedModeledCuratorFramework<T> at(ZPath path);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    CachedModeledCuratorFramework<T> resolved(T model);
 }

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index 84b9898..05886a9 100644
--- 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
@@ -33,7 +33,7 @@ public interface ModeledCache<T>
      * @param path path to the node to check
      * @return data if the node is alive, or null
      */
-    Optional<ModeledCachedNode<T>> currentData(ZPath path);
+    Optional<ZNode<T>> currentData(ZPath path);
 
     /**
      * Return the modeled current set of children at the given path, mapped by child name. There are no
@@ -42,7 +42,7 @@ public interface ModeledCache<T>
      * @param path path to the node to check
      * @return a possibly-empty list of children if the node is alive, or null
      */
-    Map<ZPath, ModeledCachedNode<T>> currentChildren(ZPath path);
+    Map<ZPath, ZNode<T>> currentChildren(ZPath path);
 
     /**
      * Return the listener container so that you can add/remove listeners

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCachedNode.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCachedNode.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCachedNode.java
deleted file mode 100644
index 68fdadc..0000000
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ModeledCachedNode.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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;
-
-/**
- * Abstracts a cached node
- */
-public interface ModeledCachedNode<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();
-}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ZNode.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ZNode.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ZNode.java
new file mode 100644
index 0000000..88f3489
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/ZNode.java
@@ -0,0 +1,49 @@
+/**
+ * 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;
+
+/**
+ * Abstracts a cached 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();
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledCuratorFrameworkImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledCuratorFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledCuratorFrameworkImpl.java
index c45d28b..a48b9bb 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledCuratorFrameworkImpl.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledCuratorFrameworkImpl.java
@@ -24,21 +24,18 @@ import org.apache.curator.framework.api.transaction.CuratorOp;
 import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
 import org.apache.curator.x.async.AsyncStage;
 import org.apache.curator.x.async.api.CreateOption;
-import org.apache.curator.x.async.modeled.CuratorModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpec;
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
+import org.apache.curator.x.async.modeled.cached.ZNode;
 import org.apache.curator.x.async.modeled.ZPath;
 import org.apache.curator.x.async.modeled.cached.CachedModeledCuratorFramework;
 import org.apache.curator.x.async.modeled.cached.ModeledCache;
-import org.apache.curator.x.async.modeled.cached.ModeledCachedNode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.server.DataTree;
-import java.util.AbstractMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramework<T>
 {
@@ -87,7 +84,7 @@ class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramew
     }
 
     @Override
-    public CuratorModelSpec<T> modelSpec()
+    public ModelSpec<T> modelSpec()
     {
         return client.modelSpec();
     }
@@ -99,15 +96,27 @@ class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramew
     }
 
     @Override
+    public CachedModeledCuratorFramework<T> at(ZPath path)
+    {
+        return new CachedModeledCuratorFrameworkImpl<>(client.at(path), cache);
+    }
+
+    @Override
+    public CachedModeledCuratorFramework<T> resolved(T model)
+    {
+        return new CachedModeledCuratorFrameworkImpl<>(client.resolved(model), cache);
+    }
+
+    @Override
     public AsyncStage<String> set(T model)
     {
-        return client.set(model);   // TODO - update cache?
+        return client.set(model);
     }
 
     @Override
     public AsyncStage<String> set(T model, Stat storingStatIn)
     {
-        return client.set(model, storingStatIn);   // TODO - update cache?
+        return client.set(model, storingStatIn);
     }
 
     @Override
@@ -120,7 +129,7 @@ class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramew
     public AsyncStage<T> read(Stat storingStatIn)
     {
         ZPath path = client.modelSpec().path();
-        Optional<ModeledCachedNode<T>> data = cache.currentData(path);
+        Optional<ZNode<T>> data = cache.currentData(path);
         return data.map(node -> {
             if ( storingStatIn != null )
             {
@@ -158,7 +167,7 @@ class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramew
     public AsyncStage<Stat> checkExists()
     {
         ZPath path = client.modelSpec().path();
-        Optional<ModeledCachedNode<T>> data = cache.currentData(path);
+        Optional<ZNode<T>> data = cache.currentData(path);
         return data.map(node -> new ModelStage<>(node.stat())).orElseGet(() -> new ModelStage<>((Stat)null));
     }
 
@@ -170,16 +179,6 @@ class CachedModeledCuratorFrameworkImpl<T> implements CachedModeledCuratorFramew
     }
 
     @Override
-    public AsyncStage<Map<ZPath, AsyncStage<T>>> readChildren()
-    {
-        Map<ZPath, AsyncStage<T>> map = cache.currentChildren(client.modelSpec().path()).entrySet()
-            .stream()
-            .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), new ModelStage<>(entry.getValue().model())))
-            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-        return new ModelStage<>(map);
-    }
-
-    @Override
     public CuratorOp createOp(T model)
     {
         return client.createOp(model);

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java
deleted file mode 100644
index 1469222..0000000
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * 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.CuratorModelSpec;
-import org.apache.curator.x.async.modeled.ModelSerializer;
-import org.apache.curator.x.async.modeled.ZPath;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.data.ACL;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-public class CuratorModelSpecImpl<T> implements CuratorModelSpec<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 Schema schema;
-
-    public CuratorModelSpecImpl(ZPath path, ModelSerializer<T> serializer, CreateMode createMode, List<ACL> aclList, Set<CreateOption> createOptions, Set<DeleteOption> deleteOptions)
-    {
-        this(path, serializer, createMode, aclList, createOptions, deleteOptions, null);
-    }
-
-    private CuratorModelSpecImpl(ZPath path, ModelSerializer<T> serializer, CreateMode createMode, List<ACL> aclList, Set<CreateOption> createOptions, Set<DeleteOption> deleteOptions, Schema schema)
-    {
-        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.schema = (schema != null) ? schema : makeSchema(); // must be last in statement in ctor
-    }
-
-    @Override
-    public CuratorModelSpec<T> at(String child)
-    {
-        return new CuratorModelSpecImpl<>(path.at(child), serializer, createMode, aclList, createOptions, deleteOptions);
-    }
-
-    @Override
-    public CuratorModelSpec<T> at(ZPath newPath)
-    {
-        return new CuratorModelSpecImpl<>(newPath, serializer, createMode, aclList, createOptions, deleteOptions);
-    }
-
-    @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 Schema schema()
-    {
-        return schema;
-    }
-
-    @Override
-    public boolean equals(Object o)
-    {
-        if ( this == o )
-        {
-            return true;
-        }
-        if ( o == null || getClass() != o.getClass() )
-        {
-            return false;
-        }
-
-        CuratorModelSpecImpl<?> that = (CuratorModelSpecImpl<?>)o;
-
-        if ( !path.equals(that.path) )
-        {
-            return false;
-        }
-        if ( !serializer.equals(that.serializer) )
-        {
-            return false;
-        }
-        if ( createMode != that.createMode )
-        {
-            return false;
-        }
-        if ( !aclList.equals(that.aclList) )
-        {
-            return false;
-        }
-        if ( !createOptions.equals(that.createOptions) )
-        {
-            return false;
-        }
-        //noinspection SimplifiableIfStatement
-        if ( !deleteOptions.equals(that.deleteOptions) )
-        {
-            return false;
-        }
-        return schema.equals(that.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 + schema.hashCode();
-        return result;
-    }
-
-    @Override
-    public String toString()
-    {
-        return "CuratorModelImpl{" + "path=" + path + ", serializer=" + serializer + ", createMode=" + createMode + ", aclList=" + aclList + ", createOptions=" + createOptions + ", deleteOptions=" + deleteOptions + ", schema=" + schema + '}';
-    }
-
-    @Override
-    public boolean isValid(Schema schema, String path, byte[] data, List<ACL> acl)
-    {
-        if ( !acl.equals(aclList) )
-        {
-            throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "ACLs do not match model ACLs");
-        }
-
-        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;
-    }
-
-    private Schema makeSchema()
-    {
-        return 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();
-    }
-}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..1ce7134
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.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.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.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSerializer;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.ACL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+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 AtomicReference<Schema> schema = new AtomicReference<>();
+    private final Function<T, String> nodeName;
+
+    public ModelSpecImpl(ZPath path, ModelSerializer<T> serializer, CreateMode createMode, List<ACL> aclList, Set<CreateOption> createOptions, Set<DeleteOption> deleteOptions, Function<T, String> nodeName)
+    {
+        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.nodeName = Objects.requireNonNull(nodeName, "nodeName cannot be null");
+    }
+
+    @Override
+    public ModelSpec<T> at(String child)
+    {
+        return at(path.at(child));
+    }
+
+    @Override
+    public ModelSpec<T> resolved(T model)
+    {
+        return at(path.at(nodeName.apply(model)));
+    }
+
+    @Override
+    public ModelSpec<T> at(ZPath newPath)
+    {
+        return new ModelSpecImpl<>(newPath, serializer, createMode, aclList, createOptions, deleteOptions, nodeName);
+    }
+
+    @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 Schema schema()
+    {
+        Schema schemaValue = schema.get();
+        if ( schemaValue == null )
+        {
+            schemaValue = makeSchema();
+            schema.compareAndSet(null, schemaValue);
+        }
+        return schemaValue;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        ModelSpecImpl<?> that = (ModelSpecImpl<?>)o;
+
+        if ( !path.equals(that.path) )
+        {
+            return false;
+        }
+        if ( !serializer.equals(that.serializer) )
+        {
+            return false;
+        }
+        if ( createMode != that.createMode )
+        {
+            return false;
+        }
+        if ( !aclList.equals(that.aclList) )
+        {
+            return false;
+        }
+        if ( !createOptions.equals(that.createOptions) )
+        {
+            return false;
+        }
+        //noinspection SimplifiableIfStatement
+        if ( !deleteOptions.equals(that.deleteOptions) )
+        {
+            return false;
+        }
+        return schema.equals(that.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 + schema.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "CuratorModelImpl{" + "path=" + path + ", serializer=" + serializer + ", createMode=" + createMode + ", aclList=" + aclList + ", createOptions=" + createOptions + ", deleteOptions=" + deleteOptions + ", schema=" + schema + '}';
+    }
+
+    @Override
+    public boolean isValid(Schema schema, String path, byte[] data, List<ACL> acl)
+    {
+        if ( !acl.equals(aclList) )
+        {
+            throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "ACLs do not match model ACLs");
+        }
+
+        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;
+    }
+
+    private Schema makeSchema()
+    {
+        return 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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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
index fb4bc70..fd03977 100644
--- 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
@@ -28,7 +28,7 @@ import org.apache.curator.x.async.modeled.ModelSerializer;
 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.cached.ModeledCachedNode;
+import org.apache.curator.x.async.modeled.cached.ZNode;
 import org.apache.zookeeper.data.Stat;
 import java.util.AbstractMap;
 import java.util.Map;
@@ -83,23 +83,23 @@ class ModeledCacheImpl<T> implements TreeCacheListener, ModeledCache<T>
     }
 
     @Override
-    public Optional<ModeledCachedNode<T>> currentData(ZPath path)
+    public Optional<ZNode<T>> currentData(ZPath path)
     {
         Entry<T> entry = entries.remove(path);
         if ( entry != null )
         {
-            return Optional.of(new InternalCachedNode<>(path, entry));
+            return Optional.of(new ZNodeImpl<>(path, entry.stat, entry.model));
         }
         return Optional.empty();
     }
 
     @Override
-    public Map<ZPath, ModeledCachedNode<T>> currentChildren(ZPath path)
+    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 InternalCachedNode<>(entry.getKey(), entry.getValue())))
+            .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));
     }
 
@@ -157,34 +157,4 @@ class ModeledCacheImpl<T> implements TreeCacheListener, ModeledCache<T>
             return null;
         });
     }
-
-    private static class InternalCachedNode<U> implements ModeledCachedNode<U>
-    {
-        private final ZPath path;
-        private final Entry<U> entry;
-
-        private InternalCachedNode(ZPath path, Entry<U> entry)
-        {
-            this.path = path;
-            this.entry = entry;
-        }
-
-        @Override
-        public ZPath path()
-        {
-            return path;
-        }
-
-        @Override
-        public Stat stat()
-        {
-            return entry.stat;
-        }
-
-        @Override
-        public U model()
-        {
-            return entry.model;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCuratorFrameworkImpl.java
----------------------------------------------------------------------
diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCuratorFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCuratorFrameworkImpl.java
index eaa4f43..202e62f 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCuratorFrameworkImpl.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledCuratorFrameworkImpl.java
@@ -32,7 +32,7 @@ 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.CuratorModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpec;
 import org.apache.curator.x.async.modeled.ModeledCuratorFramework;
 import org.apache.curator.x.async.modeled.ZPath;
 import org.apache.curator.x.async.modeled.cached.CachedModeledCuratorFramework;
@@ -40,7 +40,6 @@ import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.UnaryOperator;
@@ -50,14 +49,14 @@ public class ModeledCuratorFrameworkImpl<T> implements ModeledCuratorFramework<T
 {
     private final AsyncCuratorFramework client;
     private final WatchableAsyncCuratorFramework watchableClient;
-    private final CuratorModelSpec<T> modelSpec;
+    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;
 
-    public static <T> ModeledCuratorFrameworkImpl<T> build(CuratorFramework client, CuratorModelSpec<T> model, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter)
+    public static <T> ModeledCuratorFrameworkImpl<T> build(CuratorFramework client, ModelSpec<T> model, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter)
     {
         boolean localIsWatched = (watchMode != null);
 
@@ -82,7 +81,7 @@ public class ModeledCuratorFrameworkImpl<T> implements ModeledCuratorFramework<T
         );
     }
 
-    private ModeledCuratorFrameworkImpl(AsyncCuratorFramework client, AsyncCuratorFrameworkDsl dslClient, WatchableAsyncCuratorFramework watchableClient, CuratorModelSpec<T> modelSpec, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter)
+    private ModeledCuratorFrameworkImpl(AsyncCuratorFramework client, AsyncCuratorFrameworkDsl dslClient, WatchableAsyncCuratorFramework watchableClient, ModelSpec<T> modelSpec, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter)
     {
         this.client = client;
         this.dslClient = dslClient;
@@ -101,7 +100,7 @@ public class ModeledCuratorFrameworkImpl<T> implements ModeledCuratorFramework<T
     }
 
     @Override
-    public CuratorModelSpec<T> modelSpec()
+    public ModelSpec<T> modelSpec()
     {
         return modelSpec;
     }
@@ -221,35 +220,46 @@ public class ModeledCuratorFrameworkImpl<T> implements ModeledCuratorFramework<T
     }
 
     @Override
-    public AsyncStage<Map<ZPath, AsyncStage<T>>> readChildren()
+    public ModeledCuratorFramework<T> at(String child)
     {
-        AsyncStage<List<String>> asyncStage = watchableClient.getChildren().forPath(modelSpec.path().fullPath());
-        ModelStage<Map<ZPath, AsyncStage<T>>> modelStage = new ModelStage<>(asyncStage.event());
-        asyncStage.whenComplete((children, e) -> {
-            if ( e != null )
-            {
-                modelStage.completeExceptionally(e);
-            }
-            else
-            {
-                Map<ZPath, AsyncStage<T>> map = children
-                    .stream()
-                    .collect(Collectors.toMap(name -> modelSpec.path().at(name), name -> at(name).read()));
-                modelStage.complete(map);
-            }
-        });
-        return modelStage;
+        ModelSpec<T> newModelSpec = modelSpec.at(child);
+        return new ModeledCuratorFrameworkImpl<>(
+            client,
+            dslClient,
+            watchableClient,
+            newModelSpec,
+            watchMode,
+            watcherFilter,
+            unhandledErrorListener,
+            resultFilter
+        );
     }
 
     @Override
-    public ModeledCuratorFramework<T> at(String child)
+    public ModeledCuratorFramework<T> at(ZPath path)
+    {
+        ModelSpec<T> newModelSpec = modelSpec.at(path);
+        return new ModeledCuratorFrameworkImpl<>(
+            client,
+            dslClient,
+            watchableClient,
+            newModelSpec,
+            watchMode,
+            watcherFilter,
+            unhandledErrorListener,
+            resultFilter
+        );
+    }
+
+    @Override
+    public ModeledCuratorFramework<T> resolved(T model)
     {
-        CuratorModelSpec<T> childModel = modelSpec.at(child);
+        ModelSpec<T> newModelSpec = modelSpec.resolved(model);
         return new ModeledCuratorFrameworkImpl<>(
             client,
             dslClient,
             watchableClient,
-            childModel,
+            newModelSpec,
             watchMode,
             watcherFilter,
             unhandledErrorListener,

http://git-wip-us.apache.org/repos/asf/curator/blob/ef9df2b7/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..1390c8d
--- /dev/null
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZNodeImpl.java
@@ -0,0 +1,38 @@
+package org.apache.curator.x.async.modeled.details;
+
+import org.apache.curator.x.async.modeled.cached.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;
+    }
+}