Repository: curator
Updated Branches:
  refs/heads/CURATOR-397 [created] 8237fde0a


WIP - strongly type DSL for Curator


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

Branch: refs/heads/CURATOR-397
Commit: 8237fde0aabf7088b6ddcaf56a7fba1f8b1d3441
Parents: 191aca2
Author: randgalt <[email protected]>
Authored: Sat Apr 8 13:52:06 2017 -0500
Committer: randgalt <[email protected]>
Committed: Sat Apr 8 13:52:06 2017 -0500

----------------------------------------------------------------------
 curator-x-async/pom.xml                         |   6 +
 .../org/apache/curator/x/async/AsyncStage.java  |  15 +-
 .../x/async/modeled/JacksonModelSerializer.java |  99 ++++++++++
 .../x/async/modeled/ModelSerializer.java        |  26 +++
 .../modeled/ModeledAsyncCuratorFramework.java   |  66 +++++++
 .../ModeledAsyncCuratorFrameworkBuilder.java    | 129 +++++++++++++
 .../apache/curator/x/async/modeled/ZPath.java   |  35 ++++
 .../x/async/modeled/details/ModelStage.java     |  26 +++
 .../ModeledAsyncCuratorFrameworkImpl.java       | 186 +++++++++++++++++++
 .../x/async/modeled/details/ZPathImpl.java      | 132 +++++++++++++
 .../x/async/CompletableBaseClassForTests.java   |  65 +++++++
 .../curator/x/async/TestBasicOperations.java    |  35 +---
 .../TestModeledAsyncCuratorFramework.java       | 102 ++++++++++
 .../curator/x/async/modeled/TestZPath.java      |  39 ++++
 .../x/async/modeled/models/TestModel.java       | 115 ++++++++++++
 .../x/async/modeled/models/TestNewerModel.java  | 137 ++++++++++++++
 pom.xml                                         |   2 +-
 17 files changed, 1177 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/pom.xml
----------------------------------------------------------------------
diff --git a/curator-x-async/pom.xml b/curator-x-async/pom.xml
index a7fdcd5..fa73942 100644
--- a/curator-x-async/pom.xml
+++ b/curator-x-async/pom.xml
@@ -20,6 +20,12 @@
         </dependency>
 
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-test</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java 
b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
index 30ed234..9b63309 100644
--- a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
+++ b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java
@@ -27,9 +27,18 @@ import java.util.concurrent.CompletionStage;
 public interface AsyncStage<T> extends CompletionStage<T>
 {
     /**
-     * If the {@link 
org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is
-     * used (via {@link AsyncCuratorFramework#watched()}), this returns the 
completion
-     * stage used when the watcher is triggered
+     * <p>
+     *     If the {@link 
org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is
+     *     used (via {@link AsyncCuratorFramework#watched()}), this returns 
the completion
+     *     stage used when the watcher is triggered
+     * </p>
+     *
+     * <p>
+     *     Also, applies to {@link 
org.apache.curator.x.async.modeled.ModeledAsyncCuratorFramework}
+     *     when {@link 
org.apache.curator.x.async.modeled.ModeledAsyncCuratorFrameworkBuilder#watched(WatchMode)}
+     *     or {@link 
org.apache.curator.x.async.modeled.ModeledAsyncCuratorFrameworkBuilder#watched(WatchMode,
 java.util.function.UnaryOperator)}
+     *     is used.
+     * </p>
      *
      * @return CompletionStage for the set watcher or <code>null</code>
      */

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
new file mode 100644
index 0000000..5fb6e07
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import java.io.IOException;
+import java.util.Arrays;
+
+public class JacksonModelSerializer<T> implements ModelSerializer<T>
+{
+    private static final ObjectMapper mapper = new ObjectMapper();
+    static
+    {
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false);
+    }
+
+    private final ObjectReader reader;
+    private final ObjectWriter writer;
+
+    public static <T> JacksonModelSerializer<T> build(Class<T> modelClass)
+    {
+        return new JacksonModelSerializer<>(modelClass);
+    }
+
+    public static <T> JacksonModelSerializer<T> build(JavaType type)
+    {
+        return new JacksonModelSerializer<>(type);
+    }
+
+    public static <T> JacksonModelSerializer<T> build(TypeReference type)
+    {
+        return new JacksonModelSerializer<>(type);
+    }
+
+    public JacksonModelSerializer(Class<T> modelClass)
+    {
+        this(mapper.getTypeFactory().constructType(modelClass));
+    }
+
+    public JacksonModelSerializer(JavaType type)
+    {
+        reader = mapper.readerFor(type);
+        writer = mapper.writerFor(type);
+    }
+
+    public JacksonModelSerializer(TypeReference type)
+    {
+        reader = mapper.readerFor(type);
+        writer = mapper.writerFor(type);
+    }
+
+    @Override
+    public byte[] serialize(T model)
+    {
+        try
+        {
+            return writer.writeValueAsBytes(model);
+        }
+        catch ( JsonProcessingException e )
+        {
+            throw new RuntimeException(String.format("Could not serialize 
value: %s", model), e);
+        }
+    }
+
+    @Override
+    public T deserialize(byte[] bytes)
+    {
+        try
+        {
+            return reader.readValue(bytes);
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException(String.format("Could not deserialize 
value: %s", Arrays.toString(bytes)), e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
new file mode 100644
index 0000000..64defdf
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+public interface ModelSerializer<T>
+{
+    byte[] serialize(T model);
+
+    T deserialize(byte[] bytes);
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java
new file mode 100644
index 0000000..fdcc259
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java
@@ -0,0 +1,66 @@
+/**
+ * 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.CuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import org.apache.zookeeper.data.Stat;
+
+public interface ModeledAsyncCuratorFramework<T>
+{
+    ImmutableSet<CreateOption> defaultCreateOptions = 
ImmutableSet.of(CreateOption.createParentsAsContainers, 
CreateOption.setDataIfExists);
+    ImmutableSet<DeleteOption> defaultDeleteOptions = 
ImmutableSet.of(DeleteOption.guaranteed);
+
+    static <T> ModeledAsyncCuratorFramework<T> wrap(CuratorFramework client, 
ZPath path, ModelSerializer<T> serializer)
+    {
+        return builder(client, path, serializer).build();
+    }
+
+    static <T> ModeledAsyncCuratorFrameworkBuilder<T> builder(CuratorFramework 
client, ZPath path, ModelSerializer<T> serializer)
+    {
+        return new ModeledAsyncCuratorFrameworkBuilder<>(client, path, 
serializer)
+            .withCreateOptions(defaultCreateOptions)
+            .withDeleteOptions(defaultDeleteOptions);
+    }
+
+    CuratorFramework unwrap();
+
+    ModeledAsyncCuratorFramework<T> at(String child);
+
+    AsyncStage<String> create(T model);
+
+    AsyncStage<String> create(T model, Stat storingStatIn);
+
+    AsyncStage<T> read();
+
+    AsyncStage<T> read(Stat storingStatIn);
+
+    AsyncStage<Stat> update(T model);
+
+    AsyncStage<Stat> update(T model, int version);
+
+    AsyncStage<Stat> checkExists();
+
+    AsyncStage<Void> delete();
+
+    AsyncStage<Void> delete(int version);
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java
new file mode 100644
index 0000000..b328220
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java
@@ -0,0 +1,129 @@
+/**
+ * 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.CuratorFramework;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import 
org.apache.curator.x.async.modeled.details.ModeledAsyncCuratorFrameworkImpl;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.data.ACL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.UnaryOperator;
+
+public class ModeledAsyncCuratorFrameworkBuilder<T>
+{
+    private final CuratorFramework client;
+    private final ZPath path;
+    private final ModelSerializer<T> serializer;
+    private WatchMode watchMode;
+    private UnaryOperator<WatchedEvent> watcherFilter;
+    private UnhandledErrorListener unhandledErrorListener;
+    private UnaryOperator<CuratorEvent> resultFilter;
+    private CreateMode createMode;
+    private List<ACL> aclList;
+    private Set<CreateOption> createOptions;
+    private Set<DeleteOption> deleteOptions;
+
+    public ModeledAsyncCuratorFramework<T> build()
+    {
+        return new ModeledAsyncCuratorFrameworkImpl<>(
+            client,
+            path.fullPath(),
+            serializer,
+            watchMode,
+            watcherFilter,
+            unhandledErrorListener,
+            resultFilter,
+            createMode,
+            aclList,
+            createOptions,
+            deleteOptions
+        );
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> watched()
+    {
+        this.watchMode = WatchMode.stateChangeAndSuccess;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> watched(WatchMode watchMode)
+    {
+        this.watchMode = watchMode;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> watched(WatchMode watchMode, 
UnaryOperator<WatchedEvent> watcherFilter)
+    {
+        this.watchMode = watchMode;
+        this.watcherFilter = watcherFilter;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> 
withUnhandledErrorListener(UnhandledErrorListener unhandledErrorListener)
+    {
+        this.unhandledErrorListener = unhandledErrorListener;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> 
withResultFilter(UnaryOperator<CuratorEvent> resultFilter)
+    {
+        this.resultFilter = resultFilter;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> withCreateMode(CreateMode 
createMode)
+    {
+        this.createMode = createMode;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> withAclList(List<ACL> 
aclList)
+    {
+        this.aclList = aclList;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> 
withCreateOptions(Set<CreateOption> createOptions)
+    {
+        this.createOptions = (createOptions != null) ? 
ImmutableSet.copyOf(createOptions) : null;
+        return this;
+    }
+
+    public ModeledAsyncCuratorFrameworkBuilder<T> 
withDeleteOptions(Set<DeleteOption> deleteOptions)
+    {
+        this.deleteOptions = (deleteOptions != null) ? 
ImmutableSet.copyOf(deleteOptions) : null;
+        return this;
+    }
+
+    ModeledAsyncCuratorFrameworkBuilder(CuratorFramework client, ZPath path, 
ModelSerializer<T> serializer)
+    {
+        this.client = Objects.requireNonNull(client, "client cannot be null");
+        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/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
new file mode 100644
index 0000000..7e24341
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java
@@ -0,0 +1,35 @@
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.x.async.modeled.details.ZPathImpl;
+
+public interface ZPath
+{
+    ZPath root = ZPathImpl.root;
+
+    static ZPath parse(String fullPath)
+    {
+        return ZPathImpl.parse(fullPath);
+    }
+
+    static ZPath from(String... names)
+    {
+        ZPath path = root;
+        for ( String n : names )
+        {
+            path = path.at(n);
+        }
+        return path;
+    }
+
+    ZPath at(String child);
+
+    ZPath parent();
+
+    boolean isRoot();
+
+    String fullPath();
+
+    String parentPath();
+
+    String nodeName();
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
new file mode 100644
index 0000000..077bbb0
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled.details;
+
+import org.apache.curator.x.async.AsyncStage;
+import java.util.concurrent.CompletableFuture;
+
+abstract class ModelStage<T> extends CompletableFuture<T> implements 
AsyncStage<T>
+{
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java
new file mode 100644
index 0000000..40a3be8
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java
@@ -0,0 +1,186 @@
+/**
+ * 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.ImmutableSet;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.utils.ZKPaths;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.curator.x.async.api.AsyncCuratorFrameworkDsl;
+import org.apache.curator.x.async.api.AsyncPathAndBytesable;
+import org.apache.curator.x.async.api.AsyncPathable;
+import org.apache.curator.x.async.api.CreateOption;
+import org.apache.curator.x.async.api.DeleteOption;
+import org.apache.curator.x.async.api.WatchableAsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModelSerializer;
+import org.apache.curator.x.async.modeled.ModeledAsyncCuratorFramework;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.function.UnaryOperator;
+
+public class ModeledAsyncCuratorFrameworkImpl<T> implements 
ModeledAsyncCuratorFramework<T>
+{
+    private final AsyncCuratorFramework client;
+    private final WatchableAsyncCuratorFramework watchableClient;
+    private final String path;
+    private final ModelSerializer<T> serializer;
+    private final WatchMode watchMode;
+    private final UnaryOperator<WatchedEvent> watcherFilter;
+    private final UnhandledErrorListener unhandledErrorListener;
+    private final UnaryOperator<CuratorEvent> resultFilter;
+    private final CreateMode createMode;
+    private final List<ACL> aclList;
+    private final Set<CreateOption> createOptions;
+    private final Set<DeleteOption> deleteOptions;
+    private final AsyncCuratorFrameworkDsl dslClient;
+
+    public ModeledAsyncCuratorFrameworkImpl(CuratorFramework client, String 
path, ModelSerializer<T> serializer, WatchMode watchMode, 
UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener 
unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter, CreateMode 
createMode, List<ACL> aclList, Set<CreateOption> createOptions, 
Set<DeleteOption> deleteOptions)
+    {
+        boolean localIsWatched = (watchMode != null);
+
+        this.client = AsyncCuratorFramework.wrap(client);
+        this.path = Objects.requireNonNull(path, "path cannot be null");
+        this.serializer = Objects.requireNonNull(serializer, "serializer 
cannot be null");
+        this.watchMode = (watchMode != null) ? watchMode : 
WatchMode.stateChangeAndSuccess;
+        this.watcherFilter = watcherFilter;
+        this.unhandledErrorListener = unhandledErrorListener;
+        this.resultFilter = resultFilter;
+        this.createMode = (createMode != null) ? createMode : 
CreateMode.PERSISTENT;
+        this.aclList = aclList;
+        this.createOptions = (createOptions != null) ? 
ImmutableSet.copyOf(createOptions) : Collections.emptySet();
+        this.deleteOptions = (deleteOptions != null) ? 
ImmutableSet.copyOf(deleteOptions) : Collections.emptySet();
+
+        dslClient = this.client.with(this.watchMode, unhandledErrorListener, 
resultFilter, watcherFilter);
+        watchableClient = localIsWatched ? dslClient.watched() : dslClient;
+    }
+
+    @Override
+    public CuratorFramework unwrap()
+    {
+        return client.unwrap();
+    }
+
+    @Override
+    public AsyncStage<String> create(T model)
+    {
+        return create(model, null);
+    }
+
+    @Override
+    public AsyncStage<String> create(T model, Stat storingStatIn)
+    {
+        byte[] bytes = serializer.serialize(model);
+        return dslClient.create().withOptions(createOptions, createMode, 
aclList, storingStatIn).forPath(path, bytes);
+    }
+
+    @Override
+    public AsyncStage<T> read()
+    {
+        return read(null);
+    }
+
+    @Override
+    public AsyncStage<T> read(Stat storingStatIn)
+    {
+        AsyncPathable<AsyncStage<byte[]>> next;
+        if ( isCompressed() )
+        {
+            next = (storingStatIn != null) ? 
watchableClient.getData().decompressedStoringStatIn(storingStatIn) : 
watchableClient.getData().decompressed();
+        }
+        else
+        {
+            next = (storingStatIn != null) ? 
watchableClient.getData().storingStatIn(storingStatIn) : 
watchableClient.getData();
+        }
+        AsyncStage<byte[]> asyncStage = next.forPath(path);
+        ModelStage<T> modelStage = new ModelStage<T>()
+        {
+            @Override
+            public CompletionStage<WatchedEvent> event()
+            {
+                return asyncStage.event();
+            }
+        };
+        asyncStage.whenComplete((value, e) -> {
+            if ( e != null )
+            {
+                modelStage.completeExceptionally(e);
+            }
+            else
+            {
+                modelStage.complete(serializer.deserialize(value));
+            }
+        });
+        return modelStage;
+    }
+
+    @Override
+    public AsyncStage<Stat> update(T model)
+    {
+        return update(model, -1);
+    }
+
+    @Override
+    public AsyncStage<Stat> update(T model, int version)
+    {
+        byte[] bytes = serializer.serialize(model);
+        AsyncPathAndBytesable<AsyncStage<Stat>> next = isCompressed() ? 
dslClient.setData().compressedWithVersion(version) : dslClient.setData();
+        return next.forPath(path, bytes);
+    }
+
+    @Override
+    public AsyncStage<Stat> checkExists()
+    {
+        return watchableClient.checkExists().forPath(path);
+    }
+
+    @Override
+    public AsyncStage<Void> delete()
+    {
+        return delete(-1);
+    }
+
+    @Override
+    public AsyncStage<Void> delete(int version)
+    {
+        return dslClient.delete().withVersion(-1).forPath(path);
+    }
+
+    @Override
+    public ModeledAsyncCuratorFramework<T> at(String child)
+    {
+        String childPath = ZKPaths.makePath(path, child);
+        return new ModeledAsyncCuratorFrameworkImpl<>(client.unwrap(), 
childPath, serializer, watchMode, watcherFilter, unhandledErrorListener, 
resultFilter, createMode, aclList, createOptions, deleteOptions);
+    }
+
+    private boolean isCompressed()
+    {
+        return createOptions.contains(CreateOption.compress);
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
new file mode 100644
index 0000000..06a15ad
--- /dev/null
+++ 
b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java
@@ -0,0 +1,132 @@
+package org.apache.curator.x.async.modeled.details;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import org.apache.curator.utils.ZKPaths;
+import org.apache.curator.x.async.modeled.ZPath;
+import org.apache.zookeeper.common.PathUtils;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+public class ZPathImpl implements ZPath
+{
+    public static final ZPath root = new 
ZPathImpl(Collections.singletonList(ZKPaths.PATH_SEPARATOR));
+
+    private final List<String> nodes;
+
+    public static ZPath parse(String fullPath)
+    {
+        PathUtils.validatePath(fullPath);
+        List<String> nodes = ImmutableList.<String>builder()
+            .add(ZKPaths.PATH_SEPARATOR)
+            
.addAll(Splitter.on(ZKPaths.PATH_SEPARATOR).omitEmptyStrings().splitToList(fullPath))
+            .build();
+        return new ZPathImpl(nodes);
+    }
+
+    @Override
+    public ZPath at(String child)
+    {
+        return new ZPathImpl(nodes, child);
+    }
+
+    @Override
+    public ZPath parent()
+    {
+        checkRootAccess();
+        return new ZPathImpl(nodes.subList(0, nodes.size() - 1));
+    }
+
+    @Override
+    public boolean isRoot()
+    {
+        return nodes.size() == 1;
+    }
+
+    @Override
+    public String fullPath()
+    {
+        return buildFullPath(nodes.size());
+    }
+
+    @Override
+    public String parentPath()
+    {
+        checkRootAccess();
+        return buildFullPath(nodes.size() - 1);
+    }
+
+    @Override
+    public String nodeName()
+    {
+        return nodes.get(nodes.size() - 1);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        ZPathImpl zPaths = (ZPathImpl)o;
+
+        return nodes.equals(zPaths.nodes);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return nodes.hashCode();
+    }
+
+    @Override
+    public String toString()
+    {
+        return "ZPathImpl{" + "nodes=" + nodes + '}';
+    }
+
+    private ZPathImpl(List<String> nodes)
+    {
+        this.nodes = Objects.requireNonNull(nodes, "nodes cannot be null");
+    }
+
+    private ZPathImpl(List<String> nodes, String child)
+    {
+        PathUtils.validatePath(ZKPaths.PATH_SEPARATOR + child);
+        this.nodes = ImmutableList.<String>builder()
+            .addAll(nodes)
+            .add(child)
+            .build();
+    }
+
+    private void checkRootAccess()
+    {
+        if ( isRoot() )
+        {
+            throw new NoSuchElementException("The root has no parent");
+        }
+    }
+
+    private String buildFullPath(int size)
+    {
+        boolean addSeparator = false;
+        StringBuilder str = new StringBuilder();
+        for ( int i = 0; i < size; ++i )
+        {
+            if ( i > 1 )
+            {
+                str.append(ZKPaths.PATH_SEPARATOR);
+            }
+            str.append(nodes.get(i));
+        }
+        return str.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
new file mode 100644
index 0000000..232d301
--- /dev/null
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async;
+
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.Timing;
+import org.testng.Assert;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+
+public abstract class CompletableBaseClassForTests extends BaseClassForTests
+{
+    protected static final Timing timing = new Timing();
+
+    protected <T, U> void complete(CompletionStage<T> stage)
+    {
+        complete(stage, (v, e) -> {});
+    }
+
+    protected <T, U> void complete(CompletionStage<T> stage, BiConsumer<? 
super T, Throwable> handler)
+    {
+        try
+        {
+            stage.handle((v, e) -> {
+                handler.accept(v, e);
+                return null;
+            }).toCompletableFuture().get(timing.forWaiting().milliseconds(), 
TimeUnit.MILLISECONDS);
+        }
+        catch ( InterruptedException e )
+        {
+            Thread.interrupted();
+        }
+        catch ( ExecutionException e )
+        {
+            if ( e.getCause() instanceof AssertionError )
+            {
+                throw (AssertionError)e.getCause();
+            }
+            Assert.fail("get() failed", e);
+        }
+        catch ( TimeoutException e )
+        {
+            Assert.fail("get() timed out");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
index 1c4f241..0274413 100644
--- 
a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java
@@ -44,9 +44,8 @@ import static 
org.apache.curator.x.async.api.CreateOption.compress;
 import static org.apache.curator.x.async.api.CreateOption.setDataIfExists;
 import static org.apache.zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL;
 
-public class TestBasicOperations extends BaseClassForTests
+public class TestBasicOperations extends CompletableBaseClassForTests
 {
-    private static final Timing timing = new Timing();
     private AsyncCuratorFramework client;
 
     @BeforeMethod
@@ -181,36 +180,4 @@ public class TestBasicOperations extends BaseClassForTests
             Assert.assertEquals(v.getCode(), 
KeeperException.Code.CONNECTIONLOSS);
         });
     }
-
-    private <T, U> void complete(CompletionStage<T> stage)
-    {
-        complete(stage, (v, e) -> {});
-    }
-
-    private <T, U> void complete(CompletionStage<T> stage, BiConsumer<? super 
T, Throwable> handler)
-    {
-        try
-        {
-            stage.handle((v, e) -> {
-                handler.accept(v, e);
-                return null;
-            }).toCompletableFuture().get(timing.forWaiting().milliseconds(), 
TimeUnit.MILLISECONDS);
-        }
-        catch ( InterruptedException e )
-        {
-            Thread.interrupted();
-        }
-        catch ( ExecutionException e )
-        {
-            if ( e.getCause() instanceof AssertionError )
-            {
-                throw (AssertionError)e.getCause();
-            }
-            Assert.fail("get() failed", e);
-        }
-        catch ( TimeoutException e )
-        {
-            Assert.fail("get() timed out");
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java
new file mode 100644
index 0000000..03d6d7d
--- /dev/null
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.x.async.AsyncStage;
+import org.apache.curator.x.async.CompletableBaseClassForTests;
+import org.apache.curator.x.async.modeled.models.TestModel;
+import org.apache.curator.x.async.modeled.models.TestNewerModel;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import java.math.BigInteger;
+import java.util.concurrent.CountDownLatch;
+
+public class TestModeledAsyncCuratorFramework extends 
CompletableBaseClassForTests
+{
+    private static final ZPath path = ZPath.parse("/test/path");
+    private CuratorFramework rawClient;
+    private JacksonModelSerializer<TestModel> serializer;
+    private JacksonModelSerializer<TestNewerModel> newSerializer;
+
+    @BeforeMethod
+    @Override
+    public void setup() throws Exception
+    {
+        super.setup();
+
+        rawClient = 
CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 
timing.connection(), new RetryOneTime(1));
+        rawClient.start();
+
+        serializer = new JacksonModelSerializer<>(TestModel.class);
+        newSerializer = new JacksonModelSerializer<>(TestNewerModel.class);
+    }
+
+    @AfterMethod
+    @Override
+    public void teardown() throws Exception
+    {
+        CloseableUtils.closeQuietly(rawClient);
+        super.teardown();
+    }
+
+    @Test
+    public void testCrud()
+    {
+        TestModel rawModel = new TestModel("John", "Galt", "1 Galt's Gulch", 
42, BigInteger.valueOf(1));
+        TestModel rawModel2 = new TestModel("Wayne", "Rooney", "Old Trafford", 
10, BigInteger.valueOf(1));
+        ModeledAsyncCuratorFramework<TestModel> client = 
ModeledAsyncCuratorFramework.wrap(rawClient, path, serializer);
+        AsyncStage<String> stage = client.create(rawModel);
+        Assert.assertNull(stage.event());
+        complete(stage, (s, e) -> Assert.assertNotNull(s));
+        complete(client.read(), (model, e) -> Assert.assertEquals(model, 
rawModel));
+        complete(client.update(rawModel2));
+        complete(client.read(), (model, e) -> Assert.assertEquals(model, 
rawModel2));
+        complete(client.delete());
+        complete(client.checkExists(), (stat, e) -> Assert.assertNull(stat));
+    }
+
+    @Test
+    public void testBackwardCompatibility()
+    {
+        TestNewerModel rawNewModel = new TestNewerModel("John", "Galt", "1 
Galt's Gulch", 42, BigInteger.valueOf(1), 100);
+        ModeledAsyncCuratorFramework<TestNewerModel> clientForNew = 
ModeledAsyncCuratorFramework.wrap(rawClient, path, newSerializer);
+        complete(clientForNew.create(rawNewModel), (s, e) -> 
Assert.assertNotNull(s));
+
+        ModeledAsyncCuratorFramework<TestModel> clientForOld = 
ModeledAsyncCuratorFramework.wrap(rawClient, path, serializer);
+        complete(clientForOld.read(), (model, e) -> 
Assert.assertTrue(rawNewModel.equalsOld(model)));
+    }
+
+    @Test
+    public void testWatched() throws InterruptedException
+    {
+        CountDownLatch latch = new CountDownLatch(1);
+        ModeledAsyncCuratorFramework<TestModel> client = 
ModeledAsyncCuratorFramework.builder(rawClient, path, 
serializer).watched().build();
+        client.checkExists().event().whenComplete((event, ex) -> 
latch.countDown());
+        timing.sleepABit();
+        Assert.assertEquals(latch.getCount(), 1);
+        client.create(new TestModel());
+        Assert.assertTrue(timing.awaitLatch(latch));
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
new file mode 100644
index 0000000..9e7a718
--- /dev/null
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java
@@ -0,0 +1,39 @@
+package org.apache.curator.x.async.modeled;
+
+import org.apache.curator.utils.ZKPaths;
+import org.apache.curator.x.async.modeled.details.ZPathImpl;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestZPath
+{
+    @Test
+    public void testRoot()
+    {
+        Assert.assertEquals(ZPath.root.nodeName(), ZKPaths.PATH_SEPARATOR);
+        Assert.assertEquals(ZPath.root, ZPathImpl.root);
+        Assert.assertTrue(ZPath.root.isRoot());
+        Assert.assertEquals(ZPath.root.at("foo").parent(), ZPath.root);
+        Assert.assertTrue(ZPath.root.at("foo").parent().isRoot());
+    }
+
+    @Test
+    public void testBasic()
+    {
+        ZPath path = ZPath.root.at("one").at("two");
+        Assert.assertFalse(path.isRoot());
+        Assert.assertEquals(path, ZPath.root.at("one").at("two"));
+        Assert.assertNotEquals(path, ZPath.root.at("onex").at("two"));
+        Assert.assertEquals(path.nodeName(), "two");
+        Assert.assertEquals(path.fullPath(), "/one/two");
+        Assert.assertEquals(path.parentPath(), "/one");
+    }
+
+    @Test
+    public void testParsing()
+    {
+        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"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
new file mode 100644
index 0000000..8a92d33
--- /dev/null
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled.models;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class TestModel
+{
+    private final String firstName;
+    private final String lastName;
+    private final String address;
+    private final int age;
+    private final BigInteger salary;
+
+    public TestModel()
+    {
+        this("", "", "", 0, BigInteger.ZERO);
+    }
+
+    public TestModel(String firstName, String lastName, String address, int 
age, BigInteger salary)
+    {
+        this.firstName = Objects.requireNonNull(firstName, "firstName cannot 
be null");
+        this.lastName = Objects.requireNonNull(lastName, "lastName cannot be 
null");
+        this.address = Objects.requireNonNull(address, "address cannot be 
null");
+        this.age = Objects.requireNonNull(age, "age cannot be null");
+        this.salary = salary;
+    }
+
+    public String getFirstName()
+    {
+        return firstName;
+    }
+
+    public String getLastName()
+    {
+        return lastName;
+    }
+
+    public String getAddress()
+    {
+        return address;
+    }
+
+    public int getAge()
+    {
+        return age;
+    }
+
+    public BigInteger getSalary()
+    {
+        return salary;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        TestModel testModel = (TestModel)o;
+
+        if ( age != testModel.age )
+        {
+            return false;
+        }
+        if ( !firstName.equals(testModel.firstName) )
+        {
+            return false;
+        }
+        if ( !lastName.equals(testModel.lastName) )
+        {
+            return false;
+        }
+        //noinspection SimplifiableIfStatement
+        if ( !address.equals(testModel.address) )
+        {
+            return false;
+        }
+        return salary.equals(testModel.salary);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = firstName.hashCode();
+        result = 31 * result + lastName.hashCode();
+        result = 31 * result + address.hashCode();
+        result = 31 * result + age;
+        result = 31 * result + salary.hashCode();
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
----------------------------------------------------------------------
diff --git 
a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
new file mode 100644
index 0000000..94e82fb
--- /dev/null
+++ 
b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.x.async.modeled.models;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class TestNewerModel
+{
+    private final String firstName;
+    private final String lastName;
+    private final String address;
+    private final int age;
+    private final BigInteger salary;
+    private final long newField;
+
+    public TestNewerModel()
+    {
+        this("", "", "", 0, BigInteger.ZERO, 0);
+    }
+
+    public TestNewerModel(String firstName, String lastName, String address, 
int age, BigInteger salary, long newField)
+    {
+        this.firstName = Objects.requireNonNull(firstName, "firstName cannot 
be null");
+        this.lastName = Objects.requireNonNull(lastName, "lastName cannot be 
null");
+        this.address = Objects.requireNonNull(address, "address cannot be 
null");
+        this.age = Objects.requireNonNull(age, "age cannot be null");
+        this.salary = salary;
+        this.newField = newField;
+    }
+
+    public String getFirstName()
+    {
+        return firstName;
+    }
+
+    public String getLastName()
+    {
+        return lastName;
+    }
+
+    public String getAddress()
+    {
+        return address;
+    }
+
+    public int getAge()
+    {
+        return age;
+    }
+
+    public BigInteger getSalary()
+    {
+        return salary;
+    }
+
+    public long getNewField()
+    {
+        return newField;
+    }
+
+    public boolean equalsOld(TestModel model)
+    {
+        return firstName.equals(model.getFirstName())
+            && lastName.equals(model.getLastName())
+            && address.equals(model.getAddress())
+            && salary.equals(model.getSalary())
+            && age == model.getAge()
+            ;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        TestNewerModel that = (TestNewerModel)o;
+
+        if ( age != that.age )
+        {
+            return false;
+        }
+        if ( newField != that.newField )
+        {
+            return false;
+        }
+        if ( !firstName.equals(that.firstName) )
+        {
+            return false;
+        }
+        if ( !lastName.equals(that.lastName) )
+        {
+            return false;
+        }
+        //noinspection SimplifiableIfStatement
+        if ( !address.equals(that.address) )
+        {
+            return false;
+        }
+        return salary.equals(that.salary);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = firstName.hashCode();
+        result = 31 * result + lastName.hashCode();
+        result = 31 * result + address.hashCode();
+        result = 31 * result + age;
+        result = 31 * result + salary.hashCode();
+        result = 31 * result + (int)(newField ^ (newField >>> 32));
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 34e724c..1f66800 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,7 +79,7 @@
         <dropwizard-version>0.7.0</dropwizard-version>
         <maven-shade-plugin-version>2.4.3</maven-shade-plugin-version>
         <slf4j-version>1.7.6</slf4j-version>
-        <clirr-maven-plugin-version>2.6.1</clirr-maven-plugin-version>
+        <clirr-maven-plugin-version>2.8</clirr-maven-plugin-version>
 
         <!-- OSGi Properties -->
         <osgi.export.package />

Reply via email to