You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@curator.apache.org by ra...@apache.org on 2017/07/08 03:32:38 UTC

[6/6] curator git commit: Squashed commit of the following:

Squashed commit of the following:

commit 588dca66e6b60161bd66488edd2df211a8e2c3a6
Merge: 7e6551f3 8b28b120
Author: randgalt <ra...@apache.org>
Date:   Fri Jul 7 22:28:15 2017 -0500

    Merge branch 'master' into CURATOR-397

commit 7e6551f3fe3f1abbcf797739df20acc5a4e5be16
Author: randgalt <ra...@apache.org>
Date:   Thu Jun 29 23:21:02 2017 -0500

    completeChildrenAsZNodes() wasn't handling empty children listsw

commit 738c3617ee4887fefcf8dd5082b9b71c9f7021db
Author: randgalt <ra...@apache.org>
Date:   Thu Jun 29 23:20:45 2017 -0500

    no need to use EnsureContainers object

commit 09f9bc06accf7c42d3a5dcf3d92c20d706e56ea6
Author: randgalt <ra...@apache.org>
Date:   Wed Jun 28 09:46:32 2017 -0500

    Added asyncEnsureContainers

commit f7d410f8e11a840a36c7bb97313ed3fb7edf73ca
Author: randgalt <ra...@apache.org>
Date:   Wed Jun 28 09:19:03 2017 -0500

    It doesn't make sense for cached() to cache the root node. So, don't

commit b83bef9de0c980dfe21c85d0c08abb1bd75a06f5
Author: randgalt <ra...@apache.org>
Date:   Wed Jun 28 00:07:29 2017 -0500

    oops - orSetData() was returning null

commit 318bed14f050dd579f76a768bcc7c3125d832049
Author: randgalt <ra...@apache.org>
Date:   Tue Jun 27 00:10:24 2017 -0500

    version of AsyncLocker.release() that ignores unheld locks

commit eeabe8b5136e8a46569ede9ea193f6e2f10153b1
Author: randgalt <ra...@apache.org>
Date:   Wed Jun 14 16:10:35 2017 -0500

    completeChildrenAsZNodes was treating zPaths as node names

commit f0bcd0476e30a876f7995d191d8811fadb1a201e
Author: randgalt <ra...@apache.org>
Date:   Tue Jun 13 07:34:25 2017 -0500

    Added lockAsyncIf to AsyncLock

commit fbf25adcf88b4198fdecd18a494b615c70bf763c
Author: randgalt <ra...@apache.org>
Date:   Sat Jun 10 22:22:08 2017 -0500

    Made the API of AsyncLocker cleaner

commit e943763f0d299b5e69e6a7f4e871024b7aa95503
Author: randgalt <ra...@apache.org>
Date:   Sat Jun 10 20:46:19 2017 -0500

    Added AsyncLocker

commit 93f11ed09e0cc6e9c6b80b1a88796d027f4f3d7a
Author: randgalt <ra...@apache.org>
Date:   Sat Jun 10 19:52:15 2017 -0500

    1. remove siblings added childrenAsZNodes
    2. Add util to unwrap stages of ZNodes

commit 55df07e6ab5417943d485e88f7efa89df0b6e52d
Author: randgalt <ra...@apache.org>
Date:   Fri Jun 9 23:55:20 2017 -0500

    Added list()

commit f32a5fb4eeb63251ad769f54f7fee255c2bee61e
Author: randgalt <ra...@apache.org>
Date:   Fri Jun 9 23:54:51 2017 -0500

    Added '0' versions of Typed classes as a convenience so that the same idiom can be used even if there are no real type arguments

commit 523f0c8095fcde7033b9caa4c5423301085b0e16
Author: randgalt <ra...@apache.org>
Date:   Fri Jun 9 18:01:58 2017 -0500

    Added readThrough methods

commit 6428a9238892eba6e9f89551ed4602d2a7c5a04f
Author: randgalt <ra...@apache.org>
Date:   Thu May 18 15:32:38 2017 +0200

    Added a test for ACLs

commit 7a15af6ac5a58f1aba5dd6569d11b3a46fc45f9d
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 17:10:43 2017 +0200

    CachedModeledFramework now handles unresolved paths where the final node is a parameter.

commit bb36c48ef420d48e345368d03e4696f22d394126
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 16:53:30 2017 +0200

    updated doc for new auto-resolve behavior

commit 3338c05c997068c93fe4f889f4d0c1ee3fc3a776
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 16:03:53 2017 +0200

    renamed at() to child()

commit 05cab2ec57208d3aa87202ff2d4f44146d5ba6b5
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 16:01:59 2017 +0200

    Updated sub/pub test to use new resolving features so that at() isn't necessary

commit 6ea7221ab48dd0597e3b0f2ba4cdc33147ff7d4d
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 15:55:31 2017 +0200

    1. Support partially resolved paths
    2. Added method to get siblings
    3. Auto resolve unresolved paths on set/update using the model being set/updated

commit 7ed263cc37d1c6eacfe676f7c78096d7086b9c59
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 14:04:57 2017 +0200

    doc fixes

commit ed00d50f81cb224f9e2690aaa425adae6b0da0bd
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 13:52:51 2017 +0200

    doc fixes

commit 6037c7b270b12dce273aa67939a78a4c00b5155f
Author: randgalt <ra...@apache.org>
Date:   Thu May 11 12:53:49 2017 +0200

    further refinement of previous change. Not all completion handlers  should be in the cache's thread. But, allow for the default async to use it

commit 396d98a51495ec1c60156dcd0d6d553644e43689
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 23:38:07 2017 +0200

    More tests, refactoring

commit 6485f1650e37e392e6122f8b13ff432ee0321666
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 19:23:00 2017 +0200

    CachedModeledFrameworkImpl wasn't using executor in right way. Completions need to happen async not setting of the value. This required creating CachedStage which proxies all non-async methods to their async counterparts using the CachedModeledFrameworkImpl's executor

commit 80ca587e5e543568f06780ad8a8f874b94333218
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 16:09:55 2017 +0200

    more tests

commit 49743a487e3c159029906bb334989e6f1c82f6a8
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 15:00:05 2017 +0200

    Allow the {id} in ZPaths to use any value. This is very useful for making paths easier to read and debug. So, you can now do: '/root/{org}/employee/{emp id}'

commit cba43b342400461e0d4f0dfd894b69a8b1891438
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 14:09:12 2017 +0200

    doc updates

commit 13e17cbbfe089554f47d64449eea1302b9b6bd19
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 12:43:59 2017 +0200

    minor update

commit a7d2e058e4fedb00c408694f185b54ec4259bf94
Author: randgalt <ra...@apache.org>
Date:   Wed May 10 11:58:04 2017 +0200

    final versioned APIs and doc

commit beac06f136f26dd3dabb754dcb999876d614244b
Author: randgalt <ra...@apache.org>
Date:   Tue May 9 23:16:54 2017 +0200

    Added Versioned facade for easy management of models with versions

commit 23a1487984b576b878e3a88287f74d9381e5dec7
Author: randgalt <ra...@apache.org>
Date:   Mon May 8 19:11:22 2017 +0200

    Work on testing modeled schema

commit 555e1d4684e233e1caa910be67a4ff892253d9c7
Merge: e4a7e091 32a7755b
Author: randgalt <ra...@apache.org>
Date:   Mon May 8 18:36:48 2017 +0200

    Merge branch 'master' into CURATOR-397

commit e4a7e09172c7d9d14081233dec88690ecba6df9e
Author: randgalt <ra...@apache.org>
Date:   Mon May 8 05:42:17 2017 +0200

    some refactoring, refinement

commit c002e22e5a613cd8426c2e8b407ab06957908526
Author: randgalt <ra...@apache.org>
Date:   Sun May 7 10:26:48 2017 +0200

    removed ZPath 'resolving'. It doesn't add much value and muddies up the code

commit e95b885ebf0240e61d8de1c2644f8fb11cc2eca5
Author: randgalt <ra...@apache.org>
Date:   Sun May 7 09:58:07 2017 +0200

    Allow setting a version for orSetData()

commit 1110ab3bbc55748c053adcd909cc0d82be84309d
Author: randgalt <ra...@apache.org>
Date:   Sun May 7 09:55:28 2017 +0200

    Support setting the version when doing a create or set operation

commit c18783924af4b4c3f11d098165c85bf313f454c2
Author: randgalt <ra...@apache.org>
Date:   Sun May 7 09:41:55 2017 +0200

    Added variant to readAsZNode

commit fe0a88b3b7b9fccf9da022bc0a13e440b1f8435c
Author: randgalt <ra...@apache.org>
Date:   Fri May 5 18:38:46 2017 -0400

    better exception handling for serialization issues

commit f2370b7710b85850936f48404c6959d45ed03626
Author: randgalt <ra...@apache.org>
Date:   Thu May 4 23:15:14 2017 -0500

    1. Allow for an Executor service to be passed to the cache
    2. CachedModeledFramework must complete the stages via a thread as the caller is expected async processing

commit 29b09ceb5d305419428760e07d572dd4b920cb74
Author: randgalt <ra...@apache.org>
Date:   Thu May 4 15:35:05 2017 -0500

    updated doc

commit 0bc3a9bd2e63bee511b0d78f584f862e875c8365
Author: randgalt <ra...@apache.org>
Date:   Thu May 4 10:30:13 2017 -0500

    some refactoring and doc

commit 1793675c136bf0ba89a33f9c5490e9ab5c7f0000
Author: randgalt <ra...@apache.org>
Date:   Thu May 4 10:16:55 2017 -0500

    Allow type model specs and typed clients to be created in "one shot". This made writing the sub-pub example simpler and easier to understand.

commit 5ac1a3314e372d8bd22ef7bba704b5621a1638b8
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 22:59:47 2017 -0500

    finished docs for the pub-sub example

commit c97fae4ef6a06301f2a794df6e23411b303f8cf1
Merge: 71e28e1a f898959e
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 22:18:38 2017 -0500

    Merge branch 'master' into CURATOR-397

commit 71e28e1a6b97171b3c54da0a4dc82564247e7f24
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 21:50:59 2017 -0500

    starting read me for the sub-pub example

commit aa86931b9392b0d6f0cf6ff161afd82e9e3749f1
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 17:02:31 2017 -0500

    added lots of doc

commit ea47c6c990c9dab9f91fa76c074d1a4251df2f9e
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 14:20:43 2017 -0500

    Another big refactoring. NodeName only needs to be used internally by ZPath as all other resolve methods forward to it. This is cleaner.

commit c8a57d5387d9d34287b9e47808f47832e7758b3e
Merge: 37927efa d7d84c55
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 08:17:36 2017 -0500

    Merge branch 'CURATOR-407' into CURATOR-397

commit 37927efa65ca755407f1032ef45cb46a11372184
Author: randgalt <ra...@apache.org>
Date:   Wed May 3 07:42:13 2017 -0500

    refactoring

commit 69f1829d0f63badb0e16ff0bd4d53f3c4a8df541
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 22:01:29 2017 -0500

    added some doc

commit 0917e314b4cbb75e24e86c942b9d6501f84e8572
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 21:37:38 2017 -0500

    pub-sub example is now working - needs copious docs

commit 713bf4670e494a74b083431929b86dbfe6bd7f75
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 20:45:23 2017 -0500

    Work-in-progress. Using example sub/pub to flesh out issues and as an integration test. Working on bugs found

commit 1fcb63a5c7f29ddbfaedfdd18273ccabff21990e
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 17:32:38 2017 -0500

    Working on strongly typed parameters plus an example that uses it

commit 26c7adbb04a8c20a7d04111253c82ddef28e4f26
Merge: ef9df2b7 ed3082ec
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 15:27:27 2017 -0500

    Merge branch 'master' into CURATOR-397

commit ef9df2b7915ced99e227e2c2cd6f9ec12c7d7309
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 12:58:50 2017 -0500

    wip

commit 56c6c85a2f26ef914574139e4c1c657b5a7573a5
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 01:11:13 2017 -0500

    fixed startsWith

commit c3db1810a10e442626ee0c2f759a3f61da19375a
Author: randgalt <ra...@apache.org>
Date:   Tue May 2 00:12:02 2017 -0500

    Abstraction for creating ZPaths with strongly typed parameters

commit 6e21af2c565d95b7c281dd5211487a0682ab1a1a
Author: randgalt <ra...@apache.org>
Date:   Mon May 1 23:07:58 2017 -0500

    refactoring

commit 2cbbf999294181dbefeccebabd9c6de867142e2c
Author: randgalt <ra...@apache.org>
Date:   Mon May 1 22:56:36 2017 -0500

    Major rework of caching. Having the wrapped caches adds little value. Focus on the integrated caching in the modeled client instance

commit e40ed181655a83e705e6d74704d2fa2d1f93a7bc
Merge: 3d593105 35f5d274
Author: randgalt <ra...@apache.org>
Date:   Mon May 1 14:55:59 2017 -0500

    Merge branch 'CURATOR-3.0' into CURATOR-397

commit 3d593105d852ece596f7a55f312528ed23ccb69f
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 19:09:28 2017 -0500

    more work on new caching apis

commit b58d1ccba2878c0b2f12928b4e957f536c24fed8
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 15:43:03 2017 -0500

    resurrected TestCachedModeledCuratorFramework

commit 8418c5604cfeb186a3bd94e310a8001ab2bd5ee6
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 15:36:56 2017 -0500

    licenses

commit 16fb7b18b77153578dd489d0e1509a11eff0c6f5
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 15:36:18 2017 -0500

    Added new method of integrated caching. Needs testing, etc.

commit a4636098f6a6a679c06f337f981fd17d6b383218
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 14:12:18 2017 -0500

    make parameter the empty string

commit 70936c8c479558f737d3e86914af58d3855aa964
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 14:08:28 2017 -0500

    Added readChildren()

commit 50b054cc6af009c721e859398efcef5023f3cd3b
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 30 14:02:07 2017 -0500

    removed integrated caching for now - it was getting too cumbersome. I have some other ideas

commit 11cb97036ebc8639bd22799d99569511deac098b
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 29 13:52:36 2017 -0500

    more examples, added transactions

commit fa6bff6e5b34960fb8df37097561d4b02d47be24
Author: randgalt <ra...@apache.org>
Date:   Fri Apr 28 12:11:08 2017 -0500

    updated example

commit 38f6306ba109a61709e88b5acad233d0ba8dff26
Author: randgalt <ra...@apache.org>
Date:   Wed Apr 26 17:05:39 2017 -0500

    Added resolving methods to the model spec too

commit 52b334c30d82227ad7da3559976fa932fbbc5763
Author: randgalt <ra...@apache.org>
Date:   Wed Apr 26 16:53:17 2017 -0500

    added a resolving version that takes suppliers for parameters

commit 22fdb298db42a10ab74902b623e1696d22c2021f
Author: randgalt <ra...@apache.org>
Date:   Wed Apr 26 16:10:59 2017 -0500

    Added some tests/fixes

commit 5ebcfa32d7b00d5c2fc2eb4268a46fea2f98083d
Author: randgalt <ra...@apache.org>
Date:   Wed Apr 26 15:45:10 2017 -0500

    start of a mechanism to have variable/parameterized paths

commit 0cafc9199f3136f4f22703793bd56c73319d3161
Author: randgalt <ra...@apache.org>
Date:   Tue Apr 25 14:29:35 2017 -0500

    fixed typo bug with the cached values in ZPath - also made them more efficient

commit 3a3e6dd2d90fd2cf70507f247d1dcc1e59204721
Author: randgalt <ra...@apache.org>
Date:   Tue Apr 25 13:30:11 2017 -0500

    Created/abstracted CuratorModelSpec so that models can be specified independently of a CuratorFramework instance, etc. i.e. they can all be created at startup. The CuratorModelSpec also can generate a Schema object

commit 63225ba7562c48d9ac53bfaa50aa8ff0a790eb9c
Author: randgalt <ra...@apache.org>
Date:   Tue Apr 25 12:33:30 2017 -0500

    basics of write-through caching added. needs more work and testing

commit 6188fe6cee6b2a433f1c686adc660216a0aa1648
Author: randgalt <ra...@apache.org>
Date:   Tue Apr 18 09:43:48 2017 -0500

    Added getChildren

commit e512b5ee52bb9515493d0c077728acd6359caabd
Merge: 691f17d0 00ffe779
Author: randgalt <ra...@apache.org>
Date:   Mon Apr 17 19:43:32 2017 -0500

    Merge branch 'CURATOR-3.0' into CURATOR-397

commit 691f17d034647359a0d4e29f04f674cf589fb00b
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 17:01:03 2017 -0500

    update example index

commit 7c8f3fb62f6c9c7042cf07e78df7c00427392278
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 16:58:58 2017 -0500

    added more examples

commit 792aef2094e215edf77bf836b0f4dc43bb2cb232
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 14:27:15 2017 -0500

    renamed ModeledCuratorFramework so it isn't so long

commit 34c594a8e6c3730f06bb159709265e0d50b389db
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 13:21:49 2017 -0500

    fixed some typos and a bad link in the examples help

commit aadb72b62d6c781704f4ac977717c07a86a2694d
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 08:53:05 2017 -0500

    cleaned up ModeledCacheEvent definition

commit bf43232bd9f30efa0482203f895e4adb207ab247
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 08:47:02 2017 -0500

    doc

commit e0a27daef0b4dd363171b9ffde6eb4230cd8efd6
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 07:03:50 2017 -0500

    Make ModeledNodeCache use the same listener as the others

commit 301e989268a81537645bbdc3c71af4166550abd1
Author: randgalt <ra...@apache.org>
Date:   Sun Apr 9 06:40:34 2017 -0500

    finished initial tests

commit d518417e5c1bd2d5e4cd1a31833a28eb9bc9bc0a
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 23:29:39 2017 -0500

    tests

commit 45daa69bbf2a9ca0cfaa4c1ac20d53ce0314feb9
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 23:00:16 2017 -0500

    ModeledDetails isn't needed

commit dd390b6e1b100add85dd9d340dfbae9873b84bd0
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 22:35:51 2017 -0500

    doc

commit e8c68188d04df30eb91751f09360bb267fbe1c25
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 22:35:33 2017 -0500

    doc

commit 019caeea6aba36e77c64073003e66d62aa60b761
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 22:14:06 2017 -0500

    added tree cache wrapper

commit 4efc38f3d16d111c3a96c4eb28212b2cc8a08188
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 21:36:07 2017 -0500

    wip on wrapping caches

commit 0f0db1c386471dd4babdf3f26b37b1d9056b3f7d
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 15:15:12 2017 -0500

    more doc

commit 1dab81b5afedc4a0669312b386936baf7715c080
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 15:03:49 2017 -0500

    adding docs

commit 8237fde0aabf7088b6ddcaf56a7fba1f8b1d3441
Author: randgalt <ra...@apache.org>
Date:   Sat Apr 8 13:52:06 2017 -0500

    WIP - strongly type DSL for Curator


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

Branch: refs/heads/master
Commit: 0f5d10da38626494ae4d0e3339a2a35f270b0edd
Parents: 8b28b12
Author: randgalt <ra...@apache.org>
Authored: Fri Jul 7 22:31:42 2017 -0500
Committer: randgalt <ra...@apache.org>
Committed: Fri Jul 7 22:31:42 2017 -0500

----------------------------------------------------------------------
 curator-examples/pom.xml                        |  23 +-
 .../src/main/java/async/AsyncExamples.java      | 127 ++++++
 .../src/main/java/modeled/ContainerType.java    |  68 +++
 .../java/modeled/ModeledCuratorExamples.java    |  67 +++
 .../java/modeled/ModeledCuratorExamplesAlt.java |  52 +++
 .../src/main/java/modeled/PersonId.java         |  70 +++
 .../src/main/java/modeled/PersonModel.java      | 120 +++++
 .../src/main/java/modeled/PersonModelSpec.java  |  46 ++
 .../src/main/java/pubsub/Clients.java           |  76 ++++
 .../src/main/java/pubsub/Publisher.java         | 146 ++++++
 curator-examples/src/main/java/pubsub/README.md |  93 ++++
 .../src/main/java/pubsub/SubPubTest.java        | 220 +++++++++
 .../src/main/java/pubsub/Subscriber.java        |  84 ++++
 .../java/pubsub/messages/LocationAvailable.java |  55 +++
 .../main/java/pubsub/messages/UserCreated.java  |  64 +++
 .../src/main/java/pubsub/models/Group.java      |  47 ++
 .../src/main/java/pubsub/models/Instance.java   |  76 ++++
 .../main/java/pubsub/models/InstanceType.java   |  27 ++
 .../src/main/java/pubsub/models/Message.java    |  67 +++
 .../src/main/java/pubsub/models/Priority.java   |  26 ++
 .../src/main/resources/log4j.properties         |  24 +
 .../src/site/confluence/index.confluence        |   5 +-
 .../curator/framework/api/CreateBuilder.java    |  10 +
 .../framework/imps/CreateBuilderImpl.java       |  17 +-
 .../apache/curator/framework/schema/Schema.java |   2 +-
 .../framework/recipes/cache/NodeCache.java      |  15 +
 .../recipes/cache/PathChildrenCache.java        |   2 +-
 .../apache/curator/test/BaseClassForTests.java  |  10 +-
 curator-x-async/pom.xml                         |  18 +
 .../org/apache/curator/x/async/AsyncStage.java  |  15 +-
 .../apache/curator/x/async/AsyncWrappers.java   | 297 ++++++++++++
 .../curator/x/async/api/AsyncCreateBuilder.java |  28 ++
 .../x/async/api/AsyncCuratorFrameworkDsl.java   |   1 -
 .../x/async/details/AsyncCreateBuilderImpl.java |  21 +
 .../x/async/modeled/JacksonModelSerializer.java | 124 +++++
 .../x/async/modeled/ModelSerializer.java        |  43 ++
 .../curator/x/async/modeled/ModelSpec.java      | 217 +++++++++
 .../x/async/modeled/ModelSpecBuilder.java       | 138 ++++++
 .../x/async/modeled/ModeledFramework.java       | 371 +++++++++++++++
 .../async/modeled/ModeledFrameworkBuilder.java  | 154 +++++++
 .../curator/x/async/modeled/NodeName.java       |  39 ++
 .../curator/x/async/modeled/Resolvable.java     |  48 ++
 .../apache/curator/x/async/modeled/ZNode.java   |  74 +++
 .../apache/curator/x/async/modeled/ZPath.java   | 279 ++++++++++++
 .../modeled/cached/CachedModeledFramework.java  | 123 +++++
 .../x/async/modeled/cached/ModeledCache.java    |  46 ++
 .../modeled/cached/ModeledCacheListener.java    | 106 +++++
 .../details/CachedModeledFrameworkImpl.java     | 342 ++++++++++++++
 .../x/async/modeled/details/ModelSpecImpl.java  | 239 ++++++++++
 .../x/async/modeled/details/ModelStage.java     | 171 +++++++
 .../async/modeled/details/ModeledCacheImpl.java | 211 +++++++++
 .../modeled/details/ModeledFrameworkImpl.java   | 450 +++++++++++++++++++
 .../details/VersionedModeledFrameworkImpl.java  |  85 ++++
 .../x/async/modeled/details/ZNodeImpl.java      |  56 +++
 .../x/async/modeled/details/ZPathImpl.java      | 289 ++++++++++++
 .../x/async/modeled/typed/TypedModelSpec.java   |  87 ++++
 .../x/async/modeled/typed/TypedModelSpec0.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec10.java |  61 +++
 .../x/async/modeled/typed/TypedModelSpec2.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec3.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec4.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec5.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec6.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec7.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec8.java  |  61 +++
 .../x/async/modeled/typed/TypedModelSpec9.java  |  61 +++
 .../modeled/typed/TypedModeledFramework.java    |  93 ++++
 .../modeled/typed/TypedModeledFramework0.java   |  63 +++
 .../modeled/typed/TypedModeledFramework10.java  |  63 +++
 .../modeled/typed/TypedModeledFramework2.java   |  63 +++
 .../modeled/typed/TypedModeledFramework3.java   |  63 +++
 .../modeled/typed/TypedModeledFramework4.java   |  63 +++
 .../modeled/typed/TypedModeledFramework5.java   |  63 +++
 .../modeled/typed/TypedModeledFramework6.java   |  63 +++
 .../modeled/typed/TypedModeledFramework7.java   |  63 +++
 .../modeled/typed/TypedModeledFramework8.java   |  63 +++
 .../modeled/typed/TypedModeledFramework9.java   |  63 +++
 .../x/async/modeled/typed/TypedZPath.java       |  92 ++++
 .../x/async/modeled/typed/TypedZPath0.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath10.java     |  52 +++
 .../x/async/modeled/typed/TypedZPath2.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath3.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath4.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath5.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath6.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath7.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath8.java      |  52 +++
 .../x/async/modeled/typed/TypedZPath9.java      |  52 +++
 .../x/async/modeled/versioned/Versioned.java    |  69 +++
 .../versioned/VersionedModeledFramework.java    |  56 +++
 .../src/site/confluence/async.confluence        | 212 +++++++++
 .../src/site/confluence/index.confluence        | 213 +--------
 .../confluence/modeled-components.confluence    | 186 ++++++++
 .../site/confluence/modeled-typed.confluence    |  89 ++++
 .../src/site/confluence/modeled.confluence      |  48 ++
 curator-x-async/src/site/site.xml               |   6 +-
 .../x/async/CompletableBaseClassForTests.java   |  65 +++
 .../curator/x/async/TestAsyncWrappers.java      |  73 +++
 .../curator/x/async/TestBasicOperations.java    |  35 +-
 .../modeled/TestCachedModeledFramework.java     | 167 +++++++
 .../x/async/modeled/TestModeledFramework.java   | 178 ++++++++
 .../async/modeled/TestModeledFrameworkBase.java |  64 +++
 .../curator/x/async/modeled/TestZPath.java      | 126 ++++++
 .../x/async/modeled/models/TestModel.java       | 115 +++++
 .../x/async/modeled/models/TestNewerModel.java  | 137 ++++++
 .../x/async/modeled/models/TestSimpleModel.java |  84 ++++
 .../src/test/resources/log4j.properties         |  27 ++
 curator-x-rpc/src/site/site.xml                 |   2 +-
 pom.xml                                         |  12 +
 src/site/site.xml                               |   4 +-
 110 files changed, 9380 insertions(+), 244 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/pom.xml
----------------------------------------------------------------------
diff --git a/curator-examples/pom.xml b/curator-examples/pom.xml
index f612cc2..cc65570 100644
--- a/curator-examples/pom.xml
+++ b/curator-examples/pom.xml
@@ -50,9 +50,30 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-x-async</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/async/AsyncExamples.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/async/AsyncExamples.java b/curator-examples/src/main/java/async/AsyncExamples.java
new file mode 100644
index 0000000..43db219
--- /dev/null
+++ b/curator-examples/src/main/java/async/AsyncExamples.java
@@ -0,0 +1,127 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package async;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.AsyncEventException;
+import org.apache.curator.x.async.WatchMode;
+import org.apache.zookeeper.WatchedEvent;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * Examples using the asynchronous DSL
+ */
+public class AsyncExamples
+{
+    public static AsyncCuratorFramework wrap(CuratorFramework client)
+    {
+        // wrap a CuratorFramework instance so that it can be used async.
+        // do this once and re-use the returned AsyncCuratorFramework instance
+        return AsyncCuratorFramework.wrap(client);
+    }
+
+    public static void create(CuratorFramework client, String path, byte[] payload)
+    {
+        AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);   // normally you'd wrap early in your app and reuse the instance
+
+        // create a node at the given path with the given payload asynchronously
+        async.create().forPath(path, payload).whenComplete((name, exception) -> {
+            if ( exception != null )
+            {
+                // there was a problem
+                exception.printStackTrace();
+            }
+            else
+            {
+                System.out.println("Created node name is: " + name);
+            }
+        });
+    }
+
+    public static void createThenWatch(CuratorFramework client, String path)
+    {
+        AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);   // normally you'd wrap early in your app and reuse the instance
+
+        // this example shows to asynchronously use watchers for both event
+        // triggering and connection problems. If you don't need to be notified
+        // of connection problems, use the simpler approach shown in createThenWatchSimple()
+
+        // create a node at the given path with the given payload asynchronously
+        // then watch the created node
+        async.create().forPath(path).whenComplete((name, exception) -> {
+            if ( exception != null )
+            {
+                // there was a problem creating the node
+                exception.printStackTrace();
+            }
+            else
+            {
+                handleWatchedStage(async.watched().checkExists().forPath(path).event());
+            }
+        });
+    }
+
+    public static void createThenWatchSimple(CuratorFramework client, String path)
+    {
+        AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);   // normally you'd wrap early in your app and reuse the instance
+
+        // create a node at the given path with the given payload asynchronously
+        // then watch the created node
+        async.create().forPath(path).whenComplete((name, exception) -> {
+            if ( exception != null )
+            {
+                // there was a problem creating the node
+                exception.printStackTrace();
+            }
+            else
+            {
+                // because "WatchMode.successOnly" is used the watch stage is only triggered when
+                // the EventType is a node event
+                async.with(WatchMode.successOnly).watched().checkExists().forPath(path).event().thenAccept(event -> {
+                    System.out.println(event.getType());
+                    System.out.println(event);
+                });
+            }
+        });
+    }
+
+    private static void handleWatchedStage(CompletionStage<WatchedEvent> watchedStage)
+    {
+        // async handling of Watchers is complicated because watchers can trigger multiple times
+        // and CompletionStage don't support this behavior
+
+        // thenAccept() handles normal watcher triggering.
+        watchedStage.thenAccept(event -> {
+            System.out.println(event.getType());
+            System.out.println(event);
+            // etc.
+        });
+
+        // exceptionally is called if there is a connection problem in which case
+        // watchers trigger to signal the connection problem. "reset()" must be called
+        // to reset the watched stage
+        watchedStage.exceptionally(exception -> {
+            AsyncEventException asyncEx = (AsyncEventException)exception;
+            asyncEx.printStackTrace();    // handle the error as needed
+            handleWatchedStage(asyncEx.reset());
+            return null;
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ContainerType.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ContainerType.java b/curator-examples/src/main/java/modeled/ContainerType.java
new file mode 100644
index 0000000..a36cfaa
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ContainerType.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package modeled;
+
+public class ContainerType
+{
+    private final int typeId;
+
+    public ContainerType()
+    {
+        this(0);
+    }
+
+    public ContainerType(int typeId)
+    {
+        this.typeId = typeId;
+    }
+
+    public int getTypeId()
+    {
+        return typeId;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        ContainerType that = (ContainerType)o;
+
+        return typeId == that.typeId;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return typeId;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "ContainerType{" + "typeId=" + typeId + '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java b/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
new file mode 100644
index 0000000..e7e363c
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package modeled;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZPath;
+import java.util.function.Consumer;
+
+public class ModeledCuratorExamples
+{
+    public static ModeledFramework<PersonModel> wrap(AsyncCuratorFramework client)
+    {
+        JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
+
+        // build a model specification - you can pre-build all the model specifications for your app at startup
+        ModelSpec<PersonModel> modelSpec = ModelSpec.builder(ZPath.parse("/example/path"), serializer).build();
+
+        // wrap a CuratorFramework instance so that it can be used "modeled".
+        // do this once and re-use the returned ModeledFramework instance.
+        // ModeledFramework instances are tied to a given path
+        return ModeledFramework.wrap(client, modelSpec);
+    }
+
+    public static void createOrUpdate(ModeledFramework<PersonModel> modeled, PersonModel model)
+    {
+        // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}"
+        ModeledFramework<PersonModel> atId = modeled.child(model.getId().getId());
+
+        // by default ModeledFramework instances update the node if it already exists
+        // so this will either create or update the node
+        atId.set(model); // note - this is async
+    }
+
+    public static void readPerson(ModeledFramework<PersonModel> modeled, String id, Consumer<PersonModel> receiver)
+    {
+        // read the person with the given ID and asynchronously call the receiver after it is read
+        modeled.child(id).read().whenComplete((person, exception) -> {
+            if ( exception != null )
+            {
+                exception.printStackTrace();    // handle the error
+            }
+            else
+            {
+                receiver.accept(person);
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java b/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
new file mode 100644
index 0000000..859a1f1
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package modeled;
+
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import java.util.function.Consumer;
+
+public class ModeledCuratorExamplesAlt
+{
+    public static void createOrUpdate(PersonModelSpec modelSpec, PersonModel model)
+    {
+        // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}"
+        ModeledFramework<PersonModel> resolved = modelSpec.resolved(model.getContainerType(), model.getId());
+
+        // by default ModeledFramework instances update the node if it already exists
+        // so this will either create or update the node
+        resolved.set(model); // note - this is async
+    }
+
+    public static void readPerson(PersonModelSpec modelSpec, ContainerType containerType, PersonId id, Consumer<PersonModel> receiver)
+    {
+        ModeledFramework<PersonModel> resolved = modelSpec.resolved(containerType, id);
+
+        // read the person with the given ID and asynchronously call the receiver after it is read
+        resolved.read().whenComplete((person, exception) -> {
+            if ( exception != null )
+            {
+                exception.printStackTrace();    // handle the error
+            }
+            else
+            {
+                receiver.accept(person);
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/PersonId.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/PersonId.java b/curator-examples/src/main/java/modeled/PersonId.java
new file mode 100644
index 0000000..eabc286
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/PersonId.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package modeled;
+
+import java.util.Objects;
+
+public class PersonId
+{
+    private final String id;
+
+    public PersonId()
+    {
+        this("");
+    }
+
+    public PersonId(String id)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        PersonId personId = (PersonId)o;
+
+        return id.equals(personId.id);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return id.hashCode();
+    }
+
+    @Override
+    public String toString()
+    {
+        return "PersonId{" + "id='" + id + '\'' + '}';
+    }
+}

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

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/modeled/PersonModelSpec.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/modeled/PersonModelSpec.java b/curator-examples/src/main/java/modeled/PersonModelSpec.java
new file mode 100644
index 0000000..f90f616
--- /dev/null
+++ b/curator-examples/src/main/java/modeled/PersonModelSpec.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package modeled;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.ZPath;
+
+public class PersonModelSpec
+{
+    private final AsyncCuratorFramework client;
+    private final ModelSpec<PersonModel> modelSpec;
+
+    public PersonModelSpec(AsyncCuratorFramework client)
+    {
+        this.client = client;
+
+        JacksonModelSerializer<PersonModel> serializer = JacksonModelSerializer.build(PersonModel.class);
+        ZPath path = ZPath.parseWithIds("/example/{id}/path/{id}");
+        modelSpec = ModelSpec.builder(path, serializer).build();
+    }
+
+    public ModeledFramework<PersonModel> resolved(ContainerType containerType, PersonId personId)
+    {
+        ModelSpec<PersonModel> resolved = modelSpec.resolved(containerType.getTypeId(), personId.getId());
+        return ModeledFramework.wrap(client, resolved);
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Clients.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Clients.java b/curator-examples/src/main/java/pubsub/Clients.java
new file mode 100644
index 0000000..c0a8136
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Clients.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub;
+
+import org.apache.curator.x.async.modeled.JacksonModelSerializer;
+import org.apache.curator.x.async.modeled.ModelSpec;
+import org.apache.curator.x.async.modeled.ModelSpecBuilder;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import org.apache.zookeeper.CreateMode;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Priority;
+import java.util.concurrent.TimeUnit;
+
+public class Clients
+{
+    /**
+     * A client template for LocationAvailable instances
+     */
+    public static final TypedModeledFramework2<LocationAvailable, Group, Priority> locationAvailableClient = TypedModeledFramework2.from(
+        ModeledFramework.builder(),
+        builder(LocationAvailable.class),
+        "/root/pubsub/messages/locations/{group}/{priority}/{id}"
+    );
+
+    /**
+     * A client template for UserCreated instances
+     */
+    public static final TypedModeledFramework2<UserCreated, Group, Priority> userCreatedClient = TypedModeledFramework2.from(
+        ModeledFramework.builder(),
+        builder(UserCreated.class),
+        "/root/pubsub/messages/users/{group}/{priority}/{id}"
+    );
+
+    /**
+     * A client template for Instance instances
+     */
+    public static final TypedModeledFramework<Instance, InstanceType> instanceClient = TypedModeledFramework.from(
+        ModeledFramework.builder(),
+        builder(Instance.class),
+        "/root/pubsub/instances/{instance-type}/{id}"
+    );
+
+    private static <T> ModelSpecBuilder<T> builder(Class<T> clazz)
+    {
+        return ModelSpec.builder(JacksonModelSerializer.build(clazz))
+            .withTtl(TimeUnit.MINUTES.toMillis(10)) // for our pub-sub example, messages are valid for 10 minutes
+            .withCreateMode(CreateMode.PERSISTENT_WITH_TTL)
+            ;
+    }
+
+    private Clients()
+    {
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Publisher.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Publisher.java b/curator-examples/src/main/java/pubsub/Publisher.java
new file mode 100644
index 0000000..5c8f7af
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Publisher.java
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.ModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class Publisher
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final AsyncCuratorFramework client;
+
+    public Publisher(AsyncCuratorFramework client)
+    {
+        this.client = Objects.requireNonNull(client, "client cannot be null");
+    }
+
+    /**
+     * Publish the given instance using the Instance client template
+     *
+     * @param instance instance to publish
+     */
+    public void publishInstance(Instance instance)
+    {
+        ModeledFramework<Instance> resolvedClient = Clients.instanceClient.resolved(client, instance.getType());
+        resolvedClient.set(instance).exceptionally(e -> {
+            log.error("Could not publish instance: " + instance, e);
+            return null;
+        });
+    }
+
+    /**
+     * Publish the given instances using the Instance client template in a transaction
+     *
+     * @param instances instances to publish
+     */
+    public void publishInstances(List<Instance> instances)
+    {
+        List<CuratorOp> operations = instances.stream()
+            .map(instance -> Clients.instanceClient
+                .resolved(client, instance.getType())
+                .createOp(instance)
+            )
+            .collect(Collectors.toList());
+        client.transaction().forOperations(operations).exceptionally(e -> {
+            log.error("Could not publish instances: " + instances, e);
+            return null;
+        });
+    }
+
+    /**
+     * Publish the given LocationAvailable using the LocationAvailable client template
+     *
+     * @param group group
+     * @param locationAvailable message to publish
+     */
+    public void publishLocationAvailable(Group group, LocationAvailable locationAvailable)
+    {
+        publishMessage(Clients.locationAvailableClient, group, locationAvailable);
+    }
+
+    /**
+     * Publish the given UserCreated using the UserCreated client template
+     *
+     * @param group group
+     * @param userCreated message to publish
+     */
+    public void publishUserCreated(Group group, UserCreated userCreated)
+    {
+        publishMessage(Clients.userCreatedClient, group, userCreated);
+    }
+
+    /**
+     * Publish the given LocationAvailables using the LocationAvailable client template in a transaction
+     *
+     * @param group group
+     * @param locationsAvailable messages to publish
+     */
+    public void publishLocationsAvailable(Group group, List<LocationAvailable> locationsAvailable)
+    {
+        publishMessages(Clients.locationAvailableClient, group, locationsAvailable);
+    }
+
+    /**
+     * Publish the given UserCreateds using the UserCreated client template in a transaction
+     *
+     * @param group group
+     * @param usersCreated messages to publish
+     */
+    public void publishUsersCreated(Group group, List<UserCreated> usersCreated)
+    {
+        publishMessages(Clients.userCreatedClient, group, usersCreated);
+    }
+
+    private <T extends Message> void publishMessage(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, T message)
+    {
+        ModeledFramework<T> resolvedClient = typedClient.resolved(client, group, message.getPriority());
+        resolvedClient.set(message).exceptionally(e -> {
+            log.error("Could not publish message: " + message, e);
+            return null;
+        });
+    }
+
+    private <T extends Message> void publishMessages(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, List<T> messages)
+    {
+        List<CuratorOp> operations = messages.stream()
+            .map(message -> typedClient
+                    .resolved(client, group, message.getPriority())
+                    .createOp(message)
+                )
+            .collect(Collectors.toList());
+        client.transaction().forOperations(operations).exceptionally(e -> {
+            log.error("Could not publish messages: " + messages, e);
+            return null;
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/README.md
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/README.md b/curator-examples/src/main/java/pubsub/README.md
new file mode 100644
index 0000000..f6d5971
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/README.md
@@ -0,0 +1,93 @@
+# Pub-Sub Example
+This example models a publish and subscribe system (note: it is not meant for production) using 
+the strongly typed modeled APIs in Apache Curator. 
+
+## Design Notes
+
+In this example, there are three models that can be published: `Instance`, `LocationAvailable` 
+and `UserCreated`. Instances have an `InstanceType`; LocationAvailable and UserCreated both have 
+a `Priority` and are associated with a `Group`. (Note: these names/objects are meant for 
+illustrative purposes only and are completely contrived)
+
+Each model is stored at a unique path in ZooKeeper:
+
+* Instance: `/root/pubsub/instances/TYPE/ID`
+* LocationAvailable: `/root/pubsub/messages/locations/GROUP/PRIORITY/ID`
+* UserCreated: `/root/pubsub/messages/users/GROUP/PRIORITY/ID`
+
+All models are stored using a TTL so that they automatically get deleted after 10 minutes.
+
+## Clients
+
+This example uses the "typed" models (`TypedModelSpec`, etc.). The typed paths, models and 
+clients are meant to be created early in your application and re-used as needed. Thus, you 
+can model your ZooKeeper usage and the rest of your application can use them without worrying 
+about correct paths, types, etc.
+
+`TypedModeledFramework` is a template that produces a `ModeledFramework` by applying 
+parameters to the `TypedZPath` in the contained `TypedModelSpec`. Curator provides variants 
+that accept from 1 to 10 parameters (`TypedModeledFramework`, `TypedModeledFramework2`, 
+`TypedModeledFramework3`, etc.).
+
+In this example, the TypedModeledFrameworks are defined in `Clients.java`. E.g.
+
+```
+public static final TypedModeledFramework2<LocationAvailable, Group, Priority> locationAvailableClient = 
+    TypedModeledFramework2.from(
+        ModeledFramework.builder(),
+        builder(LocationAvailable.class),
+        "/root/pubsub/messages/locations/{group}/{priority}"
+    );
+```
+
+## Publisher
+
+`Publisher.java` shows how to use the ModeledFramework to write models. There are methods to 
+write single instances and to write lists of instances in a transaction. Each publish method 
+resolves the appropriate typed client and then calls its `set()` method with the given model.
+
+## Subscriber
+
+`Subscriber.java` uses CachedModeledFrameworks to listen for changes on the parent nodes for 
+all of the models in this example. Each of the methods resolves the appropriate typed client 
+and then starts the cache (via `cached()`).
+
+## SubPubTest
+
+`SubPubTest.java` is a class that exercises this example. 
+
+* `start()` uses `Subscriber` to start a `CachedModeledFramework` for each combination of 
+the Instance + InstanceType, LocationAvailable + Group + Priority, and UserCreated + Group + Priority. It then adds a simple listener to each cache that merely prints the class name 
+and path whenever an update occurs (see `generalListener()`).
+* `start()` also starts a scheduled task that runs every second. This task calls 
+`publishSomething()`
+* `publishSomething()` randomly publishes either a single Instance, LocationAvailable, 
+UserCreated or a list of those.
+
+`SubPubTest.java` has a `main()` method. When you run you should see something similar to this:
+
+```
+Publishing 9 instances
+Subscribed Instance @ /root/pubsub/instances/proxy/1
+Subscribed Instance @ /root/pubsub/instances/web/2
+Subscribed Instance @ /root/pubsub/instances/cache/4
+Subscribed Instance @ /root/pubsub/instances/proxy/9
+Subscribed Instance @ /root/pubsub/instances/database/3
+Subscribed Instance @ /root/pubsub/instances/cache/5
+Subscribed Instance @ /root/pubsub/instances/database/6
+Subscribed Instance @ /root/pubsub/instances/cache/7
+Subscribed Instance @ /root/pubsub/instances/cache/8
+Publishing 1 userCreated
+Subscribed UserCreated @ /root/pubsub/messages/users/main/high/10
+Publishing 9 locationsAvailable
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/low/11
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/12
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/13
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/14
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/16
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/high/15
+Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/17
+...
+```
+
+It runs for 1 minute and then exits.

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/SubPubTest.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/SubPubTest.java b/curator-examples/src/main/java/pubsub/SubPubTest.java
new file mode 100644
index 0000000..354d568
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/SubPubTest.java
@@ -0,0 +1,220 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub;
+
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.TestingServer;
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.cached.ModeledCacheListener;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Priority;
+import java.io.Closeable;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class SubPubTest implements Closeable
+{
+    private final TestingServer testingServer;
+    private final AsyncCuratorFramework client;
+    private final ScheduledExecutorService executorService;
+    private final List<CachedModeledFramework<Instance>> instanceSubscribers = new ArrayList<>();
+    private final List<CachedModeledFramework<LocationAvailable>> locationAvailableSubscribers = new ArrayList<>();
+    private final List<CachedModeledFramework<UserCreated>> userCreatedSubscribers = new ArrayList<>();
+
+    private static final AtomicLong nextId = new AtomicLong(1);
+
+    // arrays of random values used for this example
+    private static final Group[] groups = {new Group("main"), new Group("admin")};
+    private static final String[] hostnames = {"host1", "host2", "host3"};
+    private static final Integer[] ports = {80, 443, 9999};
+    private static final String[] locations = {"dc1", "dc2", "eu", "us"};
+    private static final Duration[] durations = {Duration.ofSeconds(1), Duration.ofMinutes(1), Duration.ofHours(1)};
+    private static final String[] positions = {"worker", "manager", "executive"};
+
+    public static void main(String[] args)
+    {
+        try ( SubPubTest subPubTest = new SubPubTest() )
+        {
+            subPubTest.start();
+            TimeUnit.MINUTES.sleep(1);  // run the test for a minute then exit
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public SubPubTest() throws Exception
+    {
+        this.testingServer = new TestingServer();
+        client = AsyncCuratorFramework.wrap(CuratorFrameworkFactory.newClient(testingServer.getConnectString(), new RetryOneTime(1)));
+        executorService = Executors.newSingleThreadScheduledExecutor();
+    }
+
+    public void start()
+    {
+        client.unwrap().start();
+
+        Publisher publisher = new Publisher(client);
+        Subscriber subscriber = new Subscriber(client);
+
+        // start a subscriber/cache for Instances of each InstanceType
+        instanceSubscribers.addAll(
+            Arrays.stream(InstanceType.values())
+            .map(subscriber::startInstanceSubscriber)
+            .collect(Collectors.toList())
+        );
+
+        // start a subscriber/cache for LocationAvailables of each combination of Group and Priority
+        locationAvailableSubscribers.addAll(
+            Arrays.stream(Priority.values())
+                .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startLocationAvailableSubscriber(group, priority)))
+                .collect(Collectors.toList())
+        );
+
+        // start a subscriber/cache for UserCreateds of each combination of Group and Priority
+        userCreatedSubscribers.addAll(
+            Arrays.stream(Priority.values())
+                .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startUserCreatedSubscriber(group, priority)))
+                .collect(Collectors.toList())
+        );
+
+        // add listeners for each of the caches
+        instanceSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+        locationAvailableSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+        userCreatedSubscribers.forEach(s -> s.listenable().addListener(generalListener()));
+
+        // schedule the publisher task once a second
+        executorService.scheduleAtFixedRate(() -> publishSomething(publisher), 1, 1, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        executorService.shutdownNow();
+        try
+        {
+            executorService.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch ( InterruptedException ignore )
+        {
+            Thread.currentThread().interrupt();
+        }
+
+        userCreatedSubscribers.forEach(CachedModeledFramework::close);
+        locationAvailableSubscribers.forEach(CachedModeledFramework::close);
+        instanceSubscribers.forEach(CachedModeledFramework::close);
+        client.unwrap().close();
+        testingServer.close();
+    }
+
+    private void publishSomething(Publisher publisher)
+    {
+        // randomly do some publishing - either single items or lists of items in a transaction
+        switch ( ThreadLocalRandom.current().nextInt(6) )
+        {
+            case 0:
+            {
+                Instance instance = new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports));
+                System.out.println("Publishing 1 instance");
+                publisher.publishInstance(instance);
+                break;
+            }
+
+            case 1:
+            {
+                List<Instance> instances =  IntStream.range(1, 10)
+                    .mapToObj(__ -> new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports)))
+                    .collect(Collectors.toList());
+                System.out.println(String.format("Publishing %d instances", instances.size()));
+                publisher.publishInstances(instances);
+                break;
+            }
+
+            case 2:
+            {
+                LocationAvailable locationAvailable = new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations));
+                System.out.println("Publishing 1 locationAvailable");
+                publisher.publishLocationAvailable(random(groups), locationAvailable);
+                break;
+            }
+
+            case 3:
+            {
+                List<LocationAvailable> locationsAvailable =  IntStream.range(1, 10)
+                    .mapToObj(__ -> new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations)))
+                    .collect(Collectors.toList());
+                System.out.println(String.format("Publishing %d locationsAvailable", locationsAvailable.size()));
+                publisher.publishLocationsAvailable(random(groups), locationsAvailable);
+                break;
+            }
+
+            case 4:
+            {
+                UserCreated userCreated = new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions));
+                System.out.println("Publishing 1 userCreated");
+                publisher.publishUserCreated(random(groups), userCreated);
+                break;
+            }
+
+            case 5:
+            {
+                List<UserCreated> usersCreated =  IntStream.range(1, 10)
+                    .mapToObj(__ -> new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions)))
+                    .collect(Collectors.toList());
+                System.out.println(String.format("Publishing %d usersCreated", usersCreated.size()));
+                publisher.publishUsersCreated(random(groups), usersCreated);
+                break;
+            }
+        }
+    }
+
+    private <T> ModeledCacheListener<T> generalListener()
+    {
+        return (type, path, stat, model) -> System.out.println(String.format("Subscribed %s @ %s", model.getClass().getSimpleName(), path));
+    }
+
+    @SafeVarargs
+    private final <T> T random(T... tab)
+    {
+        int index = ThreadLocalRandom.current().nextInt(tab.length);
+        return tab[index];
+    }
+
+    private String nextId()
+    {
+        return Long.toString(nextId.getAndIncrement());
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/Subscriber.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/Subscriber.java b/curator-examples/src/main/java/pubsub/Subscriber.java
new file mode 100644
index 0000000..94a6247
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/Subscriber.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub;
+
+import org.apache.curator.x.async.AsyncCuratorFramework;
+import org.apache.curator.x.async.modeled.cached.CachedModeledFramework;
+import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2;
+import pubsub.messages.LocationAvailable;
+import pubsub.messages.UserCreated;
+import pubsub.models.Group;
+import pubsub.models.Instance;
+import pubsub.models.InstanceType;
+import pubsub.models.Message;
+import pubsub.models.Priority;
+
+public class Subscriber
+{
+    private final AsyncCuratorFramework client;
+
+    public Subscriber(AsyncCuratorFramework client)
+    {
+        this.client = client;
+    }
+
+    /**
+     * Start a subscriber (a CachedModeledFramework instance) using the LocationAvailable client template
+     *
+     * @param group group to listen for
+     * @param priority priority to listen for
+     * @return CachedModeledFramework instance (already started)
+     */
+    public CachedModeledFramework<LocationAvailable> startLocationAvailableSubscriber(Group group, Priority priority)
+    {
+        return startSubscriber(Clients.locationAvailableClient, group, priority);
+    }
+
+    /**
+     * Start a subscriber (a CachedModeledFramework instance) using the UserCreated client template
+     *
+     * @param group group to listen for
+     * @param priority priority to listen for
+     * @return CachedModeledFramework instance (already started)
+     */
+    public CachedModeledFramework<UserCreated> startUserCreatedSubscriber(Group group, Priority priority)
+    {
+        return startSubscriber(Clients.userCreatedClient, group, priority);
+    }
+
+    /**
+     * Start a subscriber (a CachedModeledFramework instance) using the Instance client template
+     *
+     * @param instanceType type to listen for
+     * @return CachedModeledFramework instance (already started)
+     */
+    public CachedModeledFramework<Instance> startInstanceSubscriber(InstanceType instanceType)
+    {
+        CachedModeledFramework<Instance> resolved = Clients.instanceClient.resolved(client, instanceType).cached();
+        resolved.start();
+        return resolved;
+    }
+
+    private <T extends Message> CachedModeledFramework<T> startSubscriber(TypedModeledFramework2<T, Group, Priority> typedClient, Group group, Priority priority)
+    {
+        CachedModeledFramework<T> resolved = typedClient.resolved(client, group, priority).cached();
+        resolved.start();
+        return resolved;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java b/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
new file mode 100644
index 0000000..dd90107
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.messages;
+
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.time.Duration;
+import java.util.Objects;
+
+public class LocationAvailable extends Message
+{
+    private final String name;
+    private final Duration availableUntil;
+
+    public LocationAvailable()
+    {
+        this(Priority.low, "", Duration.ZERO);
+    }
+
+    public LocationAvailable(Priority priority, String name, Duration availableUntil)
+    {
+        super(priority);
+        this.name = Objects.requireNonNull(name, "name cannot be null");
+        this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null");
+    }
+
+    public LocationAvailable(String id, Priority priority, String name, Duration availableUntil)
+    {
+        super(id, priority);
+        this.name = Objects.requireNonNull(name, "name cannot be null");
+        this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null");
+    }
+
+    @Override
+    public String toString()
+    {
+        return "LocationAvailable{" + "name='" + name + '\'' + ", availableUntil=" + availableUntil + "} " + super.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/messages/UserCreated.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/messages/UserCreated.java b/curator-examples/src/main/java/pubsub/messages/UserCreated.java
new file mode 100644
index 0000000..bf753a8
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/messages/UserCreated.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.messages;
+
+import pubsub.models.Message;
+import pubsub.models.Priority;
+import java.util.Objects;
+
+public class UserCreated extends Message
+{
+    private final String name;
+    private final String position;
+
+    public UserCreated()
+    {
+        this(Priority.low, "","");
+    }
+
+    public UserCreated(Priority priority, String name, String position)
+    {
+        super(priority);
+        this.name = Objects.requireNonNull(name, "name cannot be null");
+        this.position = Objects.requireNonNull(position, "position cannot be null");
+    }
+
+    public UserCreated(String id, Priority priority, String name, String position)
+    {
+        super(id, priority);
+        this.name = Objects.requireNonNull(name, "name cannot be null");
+        this.position = Objects.requireNonNull(position, "position cannot be null");
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public String getPosition()
+    {
+        return position;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "UserCreated{" + "name='" + name + '\'' + ", position='" + position + '\'' + "} " + super.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Group.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Group.java b/curator-examples/src/main/java/pubsub/models/Group.java
new file mode 100644
index 0000000..07d149f
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Group.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.models;
+
+import org.apache.curator.x.async.modeled.NodeName;
+
+public class Group implements NodeName
+{
+    private final String groupName;
+
+    public Group()
+    {
+        this("");
+    }
+
+    public Group(String groupName)
+    {
+        this.groupName = groupName;
+    }
+
+    public String getGroupName()
+    {
+        return groupName;
+    }
+
+    @Override
+    public String nodeName()
+    {
+        return groupName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Instance.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Instance.java b/curator-examples/src/main/java/pubsub/models/Instance.java
new file mode 100644
index 0000000..981f113
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Instance.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.models;
+
+import org.apache.curator.x.async.modeled.NodeName;
+import java.util.Objects;
+import java.util.UUID;
+
+public class Instance implements NodeName
+{
+    private final String id;
+    private final InstanceType type;
+    private final String hostname;
+    private final int port;
+
+    public Instance()
+    {
+        this(UUID.randomUUID().toString(), InstanceType.proxy, "", 0);
+    }
+
+    public Instance(String id, InstanceType type, String hostname, int port)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+        this.type = Objects.requireNonNull(type, "type cannot be null");
+        this.hostname = Objects.requireNonNull(hostname, "hostname cannot be null");
+        this.port = port;
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public InstanceType getType()
+    {
+        return type;
+    }
+
+    public String getHostname()
+    {
+        return hostname;
+    }
+
+    public int getPort()
+    {
+        return port;
+    }
+
+    @Override
+    public String nodeName()
+    {
+        return id;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Instance{" + "id='" + id + '\'' + ", type=" + type + ", hostname='" + hostname + '\'' + ", port=" + port + '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/InstanceType.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/InstanceType.java b/curator-examples/src/main/java/pubsub/models/InstanceType.java
new file mode 100644
index 0000000..176048c
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/InstanceType.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.models;
+
+public enum InstanceType
+{
+    database,
+    cache,
+    web,
+    proxy
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/0f5d10da/curator-examples/src/main/java/pubsub/models/Message.java
----------------------------------------------------------------------
diff --git a/curator-examples/src/main/java/pubsub/models/Message.java b/curator-examples/src/main/java/pubsub/models/Message.java
new file mode 100644
index 0000000..7d92e99
--- /dev/null
+++ b/curator-examples/src/main/java/pubsub/models/Message.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package pubsub.models;
+
+import org.apache.curator.x.async.modeled.NodeName;
+import java.util.Objects;
+import java.util.UUID;
+
+public abstract class Message implements NodeName
+{
+    private final String id;
+    private final Priority priority;
+
+    protected Message()
+    {
+        this(UUID.randomUUID().toString(), Priority.low);
+    }
+
+    protected Message(Priority priority)
+    {
+        this(UUID.randomUUID().toString(), priority);
+    }
+
+    protected Message(String id, Priority priority)
+    {
+        this.id = Objects.requireNonNull(id, "id cannot be null");
+        this.priority = Objects.requireNonNull(priority, "messageType cannot be null");
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public Priority getPriority()
+    {
+        return priority;
+    }
+
+    @Override
+    public String nodeName()
+    {
+        return id;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Message{" + "id='" + id + '\'' + ", priority=" + priority + '}';
+    }
+}