Repository: curator Updated Branches: refs/heads/CURATOR-397 0bc3a9bd2 -> 29b09ceb5
updated doc Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/29b09ceb Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/29b09ceb Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/29b09ceb Branch: refs/heads/CURATOR-397 Commit: 29b09ceb5d305419428760e07d572dd4b920cb74 Parents: 0bc3a9b Author: randgalt <[email protected]> Authored: Thu May 4 15:35:05 2017 -0500 Committer: randgalt <[email protected]> Committed: Thu May 4 15:35:05 2017 -0500 ---------------------------------------------------------------------- .../src/site/confluence/index.confluence | 2 +- .../src/site/confluence/index.confluence | 29 ++-- .../confluence/modeled-cache-recipes.confluence | 46 ------- .../confluence/modeled-components.confluence | 133 +++++++++++++++++++ .../site/confluence/modeled-typed.confluence | 52 ++++++++ .../src/site/confluence/modeled.confluence | 109 ++++----------- curator-x-async/src/site/site.xml | 6 +- .../details/TestCachedModeledFramework.java | 97 -------------- src/site/site.xml | 1 + 9 files changed, 225 insertions(+), 250 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-examples/src/site/confluence/index.confluence ---------------------------------------------------------------------- diff --git a/curator-examples/src/site/confluence/index.confluence b/curator-examples/src/site/confluence/index.confluence index b9e26a7..f9be506 100644 --- a/curator-examples/src/site/confluence/index.confluence +++ b/curator-examples/src/site/confluence/index.confluence @@ -8,6 +8,6 @@ This module contains example usages of various Curator features. Each directory |/discovery|Example usage of the Curator's ServiceDiscovery| |/framework|A few examples of how to use the CuratorFramework class| |/async|Example AsyncCuratorFramework code| -|/modeled|ModeledCuratorFramework and Modeled Cache examples| +|/modeled|ModeledFramework and Modeled Cache examples| See the [examples source repo|https://github.com/apache/curator/tree/master/curator-examples/src/main/java] for each example. http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/confluence/index.confluence ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/confluence/index.confluence b/curator-x-async/src/site/confluence/index.confluence index 576b5da..4d81d44 100644 --- a/curator-x-async/src/site/confluence/index.confluence +++ b/curator-x-async/src/site/confluence/index.confluence @@ -33,28 +33,19 @@ See [[Curator Async|async.html]] for details. h2. [[Modeled Curator|modeled.html]] -This is a strongly typed DSL that allows you to map a ZooKeeper path to a serializable class as -opposed to raw byte arrays. For example: +This is a strongly typed DSL that allows you to map a Curator\-style client to: -{code} -// let "client" be a CuratorFramework instance -ModeledFramework<MyModel> modeled = ModeledCuratorFramework.wrap(client, path, serializer); -modeled.create(new MyModel()); -{code} +* A ZooKeeper path (supporting parameterized substitutions) +* A serializer for the data stored at the path +* Options for how nodes should be created (sequential, compressed data, ttl, etc.) +* ACLs for the nodes at the path +* Options for how to delete nodes (guaranteed, deleting children, etc.) -See [[Modeled Curator|modeled.html]] for details. - -h2. [[Modeled Cache Recipes|modeled-cache-recipes.html]] - -Strongly typed wrappers for Curator's Cache Recipes (NodeCache, PathChildrenCache and TreeCache) allow -you to use serializable classes as opposed to raw byte arrays. For example: +For example: {code} -ModeledNodeCache<MyModel> cache = ModeledNodeCache.wrap(nodeCache, serializer); -cache.getCurrentData().ifPresent(data -> { - MyModel model = data.getData(); - ... -); +ModeledFramework<Foo> modeled = ModeledFramework.wrap(client, fooModelSpec); +modeled.set(new Foo()); {code} -See [[Modeled Cache Recipes|modeled-cache-recipes.html]] for details. +See [[Modeled Curator|modeled.html]] for details. http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/confluence/modeled-cache-recipes.confluence ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/confluence/modeled-cache-recipes.confluence b/curator-x-async/src/site/confluence/modeled-cache-recipes.confluence deleted file mode 100644 index 684372f..0000000 --- a/curator-x-async/src/site/confluence/modeled-cache-recipes.confluence +++ /dev/null @@ -1,46 +0,0 @@ -h1. Modeled Cache Recipes - -Strongly typed wrappers for Curator's Cache Recipes (NodeCache, PathChildrenCache and TreeCache) allow -you to use serializable classes as opposed to raw byte arrays. For example: - -{code} -ModeledNodeCache<MyModel> cache = ModeledNodeCache.wrap(nodeCache, serializer); -cache.getCurrentData().ifPresent(data -> { - MyModel model = data.getData(); - ... -); -{code} - -h2. Usage - -NOTE: the modeled Cache Recipes require Curator's "curator\-recipes" module. However, to -avoid circular dependencies the internal dependency is {{provided}}. Therefore, if you wish to use -the modeled Cache Recipes wrappers you must manually add the "curator\-recipes" dependency to your build -system. - -Any of the Curator Cache Recipes — NodeCache, PathChildrenCache or TreeCache — can be wrapped -with "modeled" versions: {{ModeledNodeCache}}, {{ModeledPathChildrenCache}} and {{ModeledTreeCache}}. -Each wrapper has a {{wrap()}} method that takes an instance of the cache and a serializer. All the -public methods of each cache are duplicated in the wrapper. However, the data object and listeners -are altered to specify typed models via {{ModeledCachedNode<T>}} and {{ModeledCacheListener<T>}}. -The wrappers use the same serializer abstraction as described in [[Modeled Curator|modeled.html]]. - -h2. Example - -Here is an example of using ModeledTreeCache. The other modeled caches are similar. - -{code} -// given a model class "Person" - -ModeledTreeCache<Person> cache = ModeledTreeCache.wrap(makeTreeCache(), serializer); -ModeledCacheListener<Person> listener = event -> { - switch ( event.getType() ) { - case ModeledCacheEventType.NODE_ADDED: - Person person = event.getNode().getModel(); - handleNewPerson(person); - break; - ... - } -}; -cache.getListenable().addListener(listener); -{code} http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/confluence/modeled-components.confluence ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/confluence/modeled-components.confluence b/curator-x-async/src/site/confluence/modeled-components.confluence new file mode 100644 index 0000000..d44932a --- /dev/null +++ b/curator-x-async/src/site/confluence/modeled-components.confluence @@ -0,0 +1,133 @@ +h1. Modeled Curator \- Components + +Modeled Curator components are intended to allow you to model your ZooKeeper usage early in your application +so that the majority of the code that interacts with ZooKeeper doesn't need to be concerned with +paths, byte arrays, ACLs, options, etc. The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]] +can give you some ideas on how to accomplish this. + +h2. ZPath + +Instead of using raw string paths, Modeled Curator defines the {{ZPath}} interface that abstracts +ZooKeeper paths. ZPaths can be simple static paths or can contain parameters that can be replaced +as needed. + +To build a simple static path, use: + +{code} +ZPath path = ZPath.parse("/my/static/path"); +{code} + +To build a path with parameters, use. {{ZPath.parseWithIds()}} using the value "\{id\}" to +denote a parameter. You can then use the {{resolve()}} method to replace the parameters. E.g. + +{code} +ZPath path = ZPath.parseWithIds("/foo/{id}/bar/{id}"); + +... + +ZPath resolvedPath = path.resolve(param1, param2); +{code} + +h3. NodeName + +Parameters are resolved by calling {{toString()}} on the parameter. You can use {{NodeName}} +to change this behavior. If a parameter implements {{NodeName}} the {{nodeName()}} method +is used as the parameter value. + +h2. ModelSpec + +A {{ModelSpec}} contains all the metadata needed to operate on a ZooKeeper path: + +* A ZPath +* A serializer for the data stored at the path +* Options for how nodes should be created (sequential, compressed data, ttl, etc.) +* ACLs for the nodes at the path +* Options for how to delete nodes (guaranteed, deleting children, etc.) + +ModelSpec instances are created via a builder. The builder sets defaults that should be +desired for most applications but you can alter any of these as needed. + +{code} +// a standard model spec for the given path and serializer +// the model spec will have no ACLs and the options: +// * createParentsAsContainers +// * setDataIfExists +// * DeleteOption.guaranteed +ModelSpec<MyModel> spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)); +{code} + +As a convenience, ModelSpec provides {{resolve()}} methods in case the ZPath used has parameters. +E.g. + +{code} +ZPath path = ZPath.parseWithIds("/foo/{id}/bar/{id}"); +ModelSpec<MyModel> spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)); + +... + +ModelSpec<MyModel> resolvedSpec = spec.resolve(param1, param2); +{code} + +h2. ModeledFramework + +{{ModeledFramework}} ties together all the metadata into a Curator\-style instance that is +used to perform ZooKeeper operations. E.g. + +{code} +ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec); + +... + +MyModel instance = ... +modeledClient.set(instance); +{code} + +The "set" call in the above example is the equivalent of: + +{code} +MyModel instance = ... +String path = "/foo/bar/" + instance.getId(); +byte[] data = serializer.serialize(data); +client.create() + .withOptions(Sets.newHashSet(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists)) + .forPath(path, data); +{code} + +To get a value: + +{code} +ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec); + +... + +modeledClient.read().whenComplete((value, e) -> { + if ( e != null ) { + // handle the error + } else { + // "value" is the MyModel instance + } +}); +{code} + +The "read" call in the above example is the equivalent of: + +{code} +String path = "/foo/bar/" + instanceId; +client.getData().forPath(path).whenComplete((data, e) -> { + if ( e != null ) { + // handle the error + } else { + // NOTE: you must deal with possible deserialization problems + // caused by clients that write bad data + // If all of your code uses ModeledFramework you can guarantee that + // the data is always correctly written + MyModel model = serializer.deserialize(data); + // ... + } +}); +{code} + +h2. Caching and Typed Parameters + +In addition to the above features, Modeled Curator supports [[Integrated Caching|modeled-typed.html]] +and [[Typed Parameters|modeled-typed.html]]. See [[Caching and Typed Parameters|modeled-typed.html]] for details. http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/confluence/modeled-typed.confluence ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/confluence/modeled-typed.confluence b/curator-x-async/src/site/confluence/modeled-typed.confluence new file mode 100644 index 0000000..5b8affd --- /dev/null +++ b/curator-x-async/src/site/confluence/modeled-typed.confluence @@ -0,0 +1,52 @@ +h1. Modeled Curator \- Caching and Typed Parameters + +In addition to the [[main features|modeled-components.html]] Modeled Curator also supports +integrated caching and typed parameters. + +h2. Caching + +{{ModeledFramework}} instances can be wrapped with a facade that uses a Curator cache internally. +All read operations use this cache instead of making direct ZooKeeper calls. You can also +listen for node changes. E.g. + +{code} +ModeledFramework<MyModel> modeledClient = ModeledFramework.wrap(client, myModelSpec); +CachedModeledFramework<MyModel> cached = modeledClient.cached(); +cached.start(); + +// reads come from the cache +cached.read().whenComplete(...) ... + +cached.listenable.addListener((type, path, stat, model) -> { + // type is NODE_ADDED, NODE_UPDATED, etc. +}); +{code} + +h2. Typed Parameters + +The "resolve" methods in ZPath et al consume untyped Objects. Ideally, we should be able to +specify parameters in a strongly typed manner. Modeled Curator's "type" templates provide this. You +can specify typed parameters for ZPaths, ModelSpecs and ModeledFramework. +The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]] +shows how to use typed parameters with ModeledFramework. + +Typed interfaces are provided for up to 10 parameters and are named +{{TypedZPath}}, {{TypedZPath2}}, {{TypedModelSpec}}, {{TypedModelSpec2}}, {{TypedModeledFramework}}, +{{TypedModeledFramework2}}, etc. + +Here's an example of a TypedModeledFramework that models a Person and uses two parameters +to generate the path, a Group and a Organization: + +{code} +TypedModeledFramework2<Person, Group, Organization> clientTemplate = TypedModeledFramework2.from( + ModeledFrameworkBuilder.build(), + personModelSpec +); + +... + +ModeledFramework<Person> client = clientTemplate.resolve(group, organization); +client.set(person); +{code} + +TypedZPath and TypedModelSpec work similarly. http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/confluence/modeled.confluence ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/confluence/modeled.confluence b/curator-x-async/src/site/confluence/modeled.confluence index 8fcddf0..fcb9b28 100644 --- a/curator-x-async/src/site/confluence/modeled.confluence +++ b/curator-x-async/src/site/confluence/modeled.confluence @@ -1,103 +1,40 @@ h1. Modeled Curator -This is a strongly typed DSL that allows you to map a ZooKeeper path to a serializable class as -opposed to raw byte arrays. For example: +This is a strongly typed DSL that allows you to map a Curator\-style client to: + +* A ZooKeeper path (supporting parameterized substitutions) +* A serializer for the data stored at the path +* Options for how nodes should be created (sequential, compressed data, ttl, etc.) +* ACLs for the nodes at the path +* Options for how to delete nodes (guaranteed, deleting children, etc.) + +For example: {code} -// let "client" be a CuratorFramework instance -ModeledFramework<MyModel> modeled = ModeledCuratorFramework.wrap(client, path, serializer); -modeled.create(new MyModel()); +ModeledFramework<Foo> modeled = ModeledFramework.wrap(client, fooModelSpec); +modeled.set(new Foo()); {code} +This ModeledFramework instance knows the path to use, how to serialize the "Foo" instance, +which create options and ACLs to use, etc. + h2. Background and Usage Note: To use Modeled Curator, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage. You should also be familiar with [[Curator Async|async.html]] as Modeled Curator is based on it. -Modeled Curator maps a ZooKeeper path to a DSL that allows for strongly typed CRUD operations on that path -and any children of the path. Bound into the DSL are all the various Curator options such as whether to -watch nodes, compress data, use guaranteed delete, etc. You create an instance of this DSL once at startup -so that it can be used without client code having to remember which serializer to use, which ACLs to -set, etc - -h4. Serialization - -{{ModelSerializer}} is the interface for serializing/deserializing model instances. For convenience -a [[Jackson|https://github.com/FasterXML/jackson]] serializer is provided, {{JacksonModelSerializer}}. -However, please note that the dependency on Jackson is marked as {{provided}} so as not to introduce -a new dependency for using Curator. Therefore, if you wish to use the JacksonModelSerializer you must -manually add the dependency to your build system. - -h4. Path Abstraction +Modeled Curator consists of the components: -Instead of using raw string paths, Modeled Curator defines the {{ZPath}} interface that abstracts -ZooKeeper paths. +* [[ZPath|modeled-components.html]] +* [[ModelSpec|modeled-components.html]] +* [[ModeledFramework|modeled-components.html]] -h4. Building +Additional functionality is provided by: -You build a {{ModeledFramework}} instance using either the builder or helper wrapper. All -options needed to use the ZPath are specified at build time: - -* whether and/or how to watch the ZNode -* the ZNode {{CreateMode}} -* any {{CreateOption}}s -* any {{DeleteOption}}s -* any ACLs -* etc. - -These options are bound into the {{ModeledFramework}} instance and applied as needed. +* [[CachedModeledFramework|modeled-typed.html]] +* [[Typed Parameter Templates|modeled-typed.html]] h2. Example -For this example, assume this simple model: - -{code} -public class Person { - private final String name; - private final int age; - - public Person() { - this("", 0); - } - - public Person(String name, int age) { - this.name = name; - this.age = age; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } -} -{code} - -We can now build a {{ModeledFramework}} that manages {{Person}} instances at a given path: - -{code} -// let "client" be a CuratorFramework instance - -ZPath path = ZPath.parse(...); // whatever path you need -JacksonModelSerializer<Person> serializer = JacksonModelSerializer.build(Person.class); -ModeledCuratorFramework<MyModel> modeled = ModeledCuratorFramework.wrap(client, path, serializer); - -... - -public void writePerson(String id, Person p) { - modeled.at(id).create(p); // note this is an async operation -} - -public void readPerson(String id, Consumer<Person> receiver) { - modeled.at(id).read().whenComplete((person, exception) -> { - if ( exception != null ) { - ... - } else { - receiver.accept(person); - } - }); -} - -{code} +A complete example usage of Modeled Curator along with CachedModeledFramework and Typed Parameter Templates +can be found here: [[https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]]. http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/site/site.xml ---------------------------------------------------------------------- diff --git a/curator-x-async/src/site/site.xml b/curator-x-async/src/site/site.xml index ca6d763..f78abc7 100644 --- a/curator-x-async/src/site/site.xml +++ b/curator-x-async/src/site/site.xml @@ -25,7 +25,11 @@ <link rel="stylesheet" href="../css/site.css" /> <script type="text/javascript"> $(function(){ - $('a[title="Curator Java 8/Async"]').parent().addClass("active"); + if ( location && location.pathname && location.pathname.endsWith('/index.html') ) { + $('a[title="Java 8/Async"]').parent().addClass("active"); + } else { + $('a[title="Strongly Typed Models"]').parent().addClass("active"); + } }); </script> </head> http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledFramework.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledFramework.java deleted file mode 100644 index ae8a6bd..0000000 --- a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/details/TestCachedModeledFramework.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.curator.x.async.modeled.details; - -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.retry.RetryOneTime; -import org.apache.curator.utils.CloseableUtils; -import org.apache.curator.x.async.AsyncCuratorFramework; -import org.apache.curator.x.async.CompletableBaseClassForTests; -import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; -import org.apache.curator.x.async.modeled.ModelSpec; -import org.apache.curator.x.async.modeled.JacksonModelSerializer; -import org.apache.curator.x.async.modeled.ModelSerializer; -import org.apache.curator.x.async.modeled.ModeledFramework; -import org.apache.curator.x.async.modeled.ZPath; -import org.apache.curator.x.async.modeled.models.TestSimpleModel; -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; -import java.util.concurrent.atomic.AtomicInteger; - -public class TestCachedModeledFramework extends CompletableBaseClassForTests -{ - private static final ZPath path = ZPath.parse("/test/path"); - private CuratorFramework rawClient; - private CachedModeledFramework<TestSimpleModel> client; - - @BeforeMethod - @Override - public void setup() throws Exception - { - super.setup(); - - rawClient = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); - rawClient.start(); - - ModelSerializer<TestSimpleModel> serializer = new JacksonModelSerializer<>(TestSimpleModel.class); - client = ModeledFramework.builder(AsyncCuratorFramework.wrap(rawClient), ModelSpec.builder(path, serializer).build()).build().cached(); - } - - @AfterMethod - @Override - public void teardown() throws Exception - { - CloseableUtils.closeQuietly(rawClient); - super.teardown(); - } - - @Test - public void testBasic() throws InterruptedException - { - client.start(); - - AtomicInteger counter = new AtomicInteger(); -// ((CachedModeledCuratorFrameworkImpl)client).debugCachedReadCount = counter; - - complete(client.read()); - Assert.assertEquals(counter.get(), 0); - - complete(client.set(new TestSimpleModel("test", 10))); - Assert.assertEquals(counter.get(), 0); - - timing.sleepABit(); - - complete(client.read()); - Assert.assertEquals(counter.get(), 1); - counter.set(0); - - complete(client.set(new TestSimpleModel("test2", 20))); - Assert.assertEquals(counter.get(), 0); - - timing.sleepABit(); - - complete(client.read(), (model, e) -> Assert.assertEquals(model, new TestSimpleModel("test2", 20))); - Assert.assertEquals(counter.get(), 1); - - client.close(); - } -} http://git-wip-us.apache.org/repos/asf/curator/blob/29b09ceb/src/site/site.xml ---------------------------------------------------------------------- diff --git a/src/site/site.xml b/src/site/site.xml index 9f87bd6..83ddd46 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -74,6 +74,7 @@ <item name="Utilities" href="utilities.html"/> <item name="Client" href="curator-client/index.html"/> <item name="Java 8/Async" href="curator-x-async/index.html"/> + <item name="Strongly Typed Models" href="curator-x-async/modeled.html"/> <item name="Schema Support" href="curator-framework/schema.html"/> </menu>
