Repository: curator Updated Branches: refs/heads/CURATOR-397 7ed263cc3 -> 6ea7221ab
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 Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/6ea7221a Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/6ea7221a Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/6ea7221a Branch: refs/heads/CURATOR-397 Commit: 6ea7221ab48dd0597e3b0f2ba4cdc33147ff7d4d Parents: 7ed263c Author: randgalt <[email protected]> Authored: Thu May 11 15:55:31 2017 +0200 Committer: randgalt <[email protected]> Committed: Thu May 11 15:55:31 2017 +0200 ---------------------------------------------------------------------- .../curator/x/async/modeled/ModelSpec.java | 17 ++++++ .../x/async/modeled/ModeledFramework.java | 25 ++++++++ .../apache/curator/x/async/modeled/ZPath.java | 7 +++ .../details/CachedModeledFrameworkImpl.java | 13 +++++ .../x/async/modeled/details/ModelSpecImpl.java | 6 ++ .../modeled/details/ModeledFrameworkImpl.java | 60 ++++++++++++++++---- .../x/async/modeled/details/ZPathImpl.java | 7 +-- .../curator/x/async/modeled/TestZPath.java | 25 +++++++- 8 files changed, 140 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java index 0567635..652eabd 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSpec.java @@ -94,6 +94,23 @@ public interface ModelSpec<T> extends Resolvable ModelSpec<T> at(Object child); /** + * <p> + * Return a new CuratorModel instance with all the same options but applying to the parent node of this CuratorModel's + * path. E.g. if this CuratorModel instance applies to "/a/b/c", calling <code>modeled.parent()</code> returns an instance that applies to + * "/a/b". + * </p> + * + * <p> + * The replacement is the <code>toString()</code> value of child or, + * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, + * the value of <code>nodeName()</code>. + * </p> + * + * @return new Modeled Spec instance + */ + ModelSpec<T> parent(); + + /** * Return a new CuratorModel instance with all the same options but using the given path. * * @param path new path http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java index cb511d3..42447d8 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledFramework.java @@ -132,6 +132,23 @@ public interface ModeledFramework<T> ModeledFramework<T> at(Object child); /** + * <p> + * Return a new Modeled Curator instance with all the same options but applying to the parent node of this Modeled Curator's + * path. E.g. if this Modeled Curator instance applies to "/a/b/c", calling <code>modeled.parent()</code> returns an instance that applies to + * "/a/b". + * </p> + * + * <p> + * The replacement is the <code>toString()</code> value of child or, + * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, + * the value of <code>nodeName()</code>. + * </p> + * + * @return new Modeled Curator instance + */ + ModeledFramework<T> parent(); + + /** * Return a Modeled Curator instance with all the same options but using the given path. * * @param path new path @@ -263,6 +280,14 @@ public interface ModeledFramework<T> AsyncStage<List<ZPath>> children(); /** + * Return the child paths of this instance's parent path (in no particular order) + * + * @return AsyncStage + * @see org.apache.curator.x.async.AsyncStage + */ + AsyncStage<List<ZPath>> siblings(); + + /** * Create operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. Note: * due to ZooKeeper transaction limits, this is a _not_ a "set or update" operation but only http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java index ba6fc21..0e32023 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java @@ -241,6 +241,13 @@ public interface ZPath extends Resolvable boolean isRoot(); /** + * Return true if this path is fully resolved (i.e. has no unresoled parameters) + * + * @return true/false + */ + boolean isResolved(); + + /** * Return true if this path starts with the given path. i.e. * <code>ZPath.from("/one/two/three").startsWith(ZPath.from("/one/two"))</code> returns true * http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java index 7df2e98..4cfb70c 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CachedModeledFrameworkImpl.java @@ -129,6 +129,12 @@ class CachedModeledFrameworkImpl<T> implements CachedModeledFramework<T> } @Override + public ModeledFramework<T> parent() + { + throw new UnsupportedOperationException("Not supported for CachedModeledFramework. Instead, call parent() on the ModeledFramework before calling cached()"); + } + + @Override public CachedModeledFramework<T> withPath(ZPath path) { return new CachedModeledFrameworkImpl<>(client.withPath(path), cache, executor, asyncDefaultMode); @@ -222,6 +228,13 @@ class CachedModeledFrameworkImpl<T> implements CachedModeledFramework<T> } @Override + public AsyncStage<List<ZPath>> siblings() + { + Set<ZPath> paths = cache.currentChildren(client.modelSpec().path().parent()).keySet(); + return completed(Lists.newArrayList(paths)); + } + + @Override public CuratorOp createOp(T model) { return client.createOp(model); http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java index 847ce61..b75fa12 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelSpecImpl.java @@ -64,6 +64,12 @@ public class ModelSpecImpl<T> implements ModelSpec<T>, SchemaValidator } @Override + public ModelSpec<T> parent() + { + return withPath(path.parent()); + } + + @Override public ModelSpec<T> resolved(Object... parameters) { return withPath(path.resolved(parameters)); http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java index 7be713c..abd6af7 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledFrameworkImpl.java @@ -157,7 +157,7 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> byte[] bytes = modelSpec.serializer().serialize(item); return dslClient.create() .withOptions(modelSpec.createOptions(), modelSpec.createMode(), fixAclList(modelSpec.aclList()), storingStatIn, modelSpec.ttl(), version) - .forPath(modelSpec.path().fullPath(), bytes); + .forPath(resolveForSet(item), bytes); } catch ( Exception e ) { @@ -165,11 +165,6 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> } } - private List<ACL> fixAclList(List<ACL> aclList) - { - return (aclList.size() > 0) ? aclList : null; // workaround for old, bad design. empty list not accepted - } - @Override public AsyncStage<T> read() { @@ -201,7 +196,7 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> { byte[] bytes = modelSpec.serializer().serialize(item); AsyncPathAndBytesable<AsyncStage<Stat>> next = isCompressed() ? dslClient.setData().compressedWithVersion(version) : dslClient.setData(); - return next.forPath(modelSpec.path().fullPath(), bytes); + return next.forPath(resolveForSet(item), bytes); } catch ( Exception e ) { @@ -230,7 +225,18 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> @Override public AsyncStage<List<ZPath>> children() { - AsyncStage<List<String>> asyncStage = watchableClient.getChildren().forPath(modelSpec.path().fullPath()); + return internalGetChildren(modelSpec.path()); + } + + @Override + public AsyncStage<List<ZPath>> siblings() + { + return internalGetChildren(modelSpec.path().parent()); + } + + private AsyncStage<List<ZPath>> internalGetChildren(ZPath path) + { + AsyncStage<List<String>> asyncStage = watchableClient.getChildren().forPath(path.fullPath()); ModelStage<List<ZPath>> modelStage = ModelStage.make(asyncStage.event()); asyncStage.whenComplete((children, e) -> { if ( e != null ) @@ -239,13 +245,30 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> } else { - modelStage.complete(children.stream().map(child -> modelSpec.path().at(child)).collect(Collectors.toList())); + modelStage.complete(children.stream().map(path::at).collect(Collectors.toList())); } }); return modelStage; } @Override + public ModeledFramework<T> parent() + { + ModelSpec<T> newModelSpec = modelSpec.parent(); + return new ModeledFrameworkImpl<>( + client, + dslClient, + watchableClient, + newModelSpec, + watchMode, + watcherFilter, + unhandledErrorListener, + resultFilter, + isWatched + ); + } + + @Override public ModeledFramework<T> at(Object child) { ModelSpec<T> newModelSpec = modelSpec.at(child); @@ -290,7 +313,7 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> return client.transactionOp() .create() .withOptions(modelSpec.createMode(), fixAclList(modelSpec.aclList()), modelSpec.createOptions().contains(CreateOption.compress), modelSpec.ttl()) - .forPath(modelSpec.path().fullPath(), modelSpec.serializer().serialize(model)); + .forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } @Override @@ -305,9 +328,9 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> AsyncTransactionSetDataBuilder builder = client.transactionOp().setData(); if ( isCompressed() ) { - return builder.withVersionCompressed(version).forPath(modelSpec.path().fullPath(), modelSpec.serializer().serialize(model)); + return builder.withVersionCompressed(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } - return builder.withVersion(version).forPath(modelSpec.path().fullPath(), modelSpec.serializer().serialize(model)); + return builder.withVersion(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } @Override @@ -372,4 +395,17 @@ public class ModeledFrameworkImpl<T> implements ModeledFramework<T> return modelStage; } + private String resolveForSet(T model) + { + if ( modelSpec.path().isResolved() ) + { + return modelSpec.path().fullPath(); + } + return modelSpec.path().resolved(model).fullPath(); + } + + private List<ACL> fixAclList(List<ACL> aclList) + { + return (aclList.size() > 0) ? aclList : null; // workaround for old, bad design. empty list not accepted + } } http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java index 209b8da..35a6bd2 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java @@ -209,12 +209,8 @@ public class ZPathImpl implements ZPath Iterator<Object> iterator = parameters.iterator(); List<String> nodeNames = nodes.stream() .map(name -> { - if ( isParameter(name) ) + if ( isParameter(name) && iterator.hasNext() ) { - if ( !iterator.hasNext() ) - { - throw new IllegalStateException(String.format("Parameter missing for [%s]", toString())); - } return NodeName.nameFrom(iterator.next()); } return name; @@ -223,6 +219,7 @@ public class ZPathImpl implements ZPath return new ZPathImpl(nodeNames, null); } + @Override public boolean isResolved() { return isResolved; http://git-wip-us.apache.org/repos/asf/curator/blob/6ea7221a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java index f3d6c87..5136282 100644 --- a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java @@ -54,11 +54,11 @@ public class TestZPath Assert.assertFalse(path.startsWith(ZPath.root.at("two"))); ZPath checkIdLike = ZPath.parse("/one/{two}/three"); - Assert.assertTrue(((ZPathImpl)checkIdLike).isResolved()); + Assert.assertTrue(checkIdLike.isResolved()); checkIdLike = ZPath.parse("/one/" + ZPath.parameter() + "/three"); - Assert.assertTrue(((ZPathImpl)checkIdLike).isResolved()); + Assert.assertTrue(checkIdLike.isResolved()); checkIdLike = ZPath.parse("/one/" + ZPath.parameter("others") + "/three"); - Assert.assertTrue(((ZPathImpl)checkIdLike).isResolved()); + Assert.assertTrue(checkIdLike.isResolved()); } @Test @@ -104,4 +104,23 @@ public class TestZPath Assert.assertEquals(ZPath.from("a", parameter(), "b", parameter()).toString(), "/a/{id}/b/{id}"); Assert.assertEquals(ZPath.from("a", parameter("foo"), "b", parameter("bar")).toString(), "/a/{foo}/b/{bar}"); } + + @Test + public void testPartialResolution() + { + ZPath path = ZPath.parseWithIds("/one/{1}/two/{2}"); + Assert.assertFalse(path.parent().isResolved()); + Assert.assertFalse(path.parent().parent().isResolved()); + Assert.assertTrue(path.parent().parent().parent().isResolved()); + Assert.assertFalse(path.isResolved()); + + path = path.resolved("p1"); + Assert.assertFalse(path.isResolved()); + Assert.assertTrue(path.parent().isResolved()); + Assert.assertEquals(path.toString(), "/one/p1/two/{2}"); + + path = path.resolved("p2"); + Assert.assertTrue(path.isResolved()); + Assert.assertEquals(path.toString(), "/one/p1/two/p2"); + } }
