Repository: curator Updated Branches: refs/heads/CURATOR-397 0cafc9199 -> 5ebcfa32d
start of a mechanism to have variable/parameterized paths Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/5ebcfa32 Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/5ebcfa32 Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/5ebcfa32 Branch: refs/heads/CURATOR-397 Commit: 5ebcfa32d7b00d5c2fc2eb4268a46fea2f98083d Parents: 0cafc91 Author: randgalt <[email protected]> Authored: Wed Apr 26 15:45:10 2017 -0500 Committer: randgalt <[email protected]> Committed: Wed Apr 26 15:45:10 2017 -0500 ---------------------------------------------------------------------- .../x/async/modeled/CuratorModelSpec.java | 10 +++ .../apache/curator/x/async/modeled/ZPath.java | 29 +++++++ .../modeled/details/CuratorModelSpecImpl.java | 8 +- .../x/async/modeled/details/ZPathImpl.java | 88 ++++++++++++++------ 4 files changed, 108 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/5ebcfa32/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java index 0843e47..d491cfa 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/CuratorModelSpec.java @@ -59,6 +59,16 @@ public interface CuratorModelSpec<T> CuratorModelSpec<T> at(String child); /** + * Return a new CuratorModel instance with all the same options but applying to the given parameters of this CuratorModel's + * path via {@link org.apache.curator.x.async.modeled.ZPath#resolved(Object...)} + * + * @param parameters list of replacements. Must have be the same length as the number of + * parameter nodes in the path + * @return new Modeled Curator instance + */ + CuratorModelSpec<T> resolved(Object... parameters); + + /** * Return the model's path * * @return path http://git-wip-us.apache.org/repos/asf/curator/blob/5ebcfa32/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 0f1556b..ff84d26 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 @@ -19,6 +19,7 @@ package org.apache.curator.x.async.modeled; import org.apache.curator.x.async.modeled.details.ZPathImpl; +import java.util.regex.Pattern; /** * Abstracts a ZooKeeper ZNode path @@ -66,6 +67,27 @@ public interface ZPath } /** + * Return the special node name that can be used for replacements at runtime + * via {@link #resolved(Object...)} + * + * @return name + */ + static String parameterNodeName() + { + return ZPathImpl.parameter; + } + + /** + * When creating paths, any node in the path can be set to {@link #parameterNodeName()}. + * At runtime, the ZPath can be "resolved" by replacing these nodes with values. + * + * @param parameters list of replacements. Must have be the same length as the number of + * parameter nodes in the path + * @return new resolved ZPath + */ + ZPath resolved(Object... parameters); + + /** * Return a ZPath that represents a child ZNode of this ZPath. e.g. * <code>ZPath.from("a", "b").at("c")</code> represents the path "/a/b/c" * @@ -110,4 +132,11 @@ public interface ZPath * @return name */ String nodeName(); + + /** + * Return a regex Pattern useful for using in {@link org.apache.curator.framework.schema.Schema} + * + * @return pattern for this path + */ + Pattern toSchemaPathPattern(); } http://git-wip-us.apache.org/repos/asf/curator/blob/5ebcfa32/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java index 5522b15..863a9e3 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/CuratorModelSpecImpl.java @@ -69,6 +69,12 @@ public class CuratorModelSpecImpl<T> implements CuratorModelSpec<T>, SchemaValid } @Override + public CuratorModelSpec<T> resolved(Object... parameters) + { + return new CuratorModelSpecImpl<>(path.resolved(parameters), serializer, createMode, aclList, createOptions, deleteOptions); + } + + @Override public ZPath path() { return path; @@ -192,7 +198,7 @@ public class CuratorModelSpecImpl<T> implements CuratorModelSpec<T>, SchemaValid private Schema makeSchema() { - return Schema.builder(path.fullPath() + ZKPaths.PATH_SEPARATOR + ".*") + return Schema.builder(path.toSchemaPathPattern()) .dataValidator(this) .ephemeral(createMode.isEphemeral() ? Schema.Allowance.MUST : Schema.Allowance.CANNOT) .canBeDeleted(true) http://git-wip-us.apache.org/repos/asf/curator/blob/5ebcfa32/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 8c51e32..a4685af 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 @@ -18,8 +18,10 @@ */ package org.apache.curator.x.async.modeled.details; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.common.PathUtils; @@ -27,24 +29,25 @@ import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.regex.Pattern; public class ZPathImpl implements ZPath { - public static final ZPath root = new ZPathImpl(Collections.singletonList(ZKPaths.PATH_SEPARATOR)); + public static final ZPath root = new ZPathImpl(Collections.singletonList(ZKPaths.PATH_SEPARATOR), null); - private volatile String fullPathCache = null; - private volatile String parentPathCache = null; + public static final String parameter = "\u0000"; // PathUtils.validatePath() rejects this so it's useful for this purpose private final List<String> nodes; + private final boolean isResolved; public static ZPath parse(String fullPath) { - PathUtils.validatePath(fullPath); List<String> nodes = ImmutableList.<String>builder() .add(ZKPaths.PATH_SEPARATOR) .addAll(Splitter.on(ZKPaths.PATH_SEPARATOR).omitEmptyStrings().splitToList(fullPath)) .build(); - return new ZPathImpl(nodes); + nodes.forEach(ZPathImpl::validate); + return new ZPathImpl(nodes, null); } @Override @@ -57,7 +60,7 @@ public class ZPathImpl implements ZPath public ZPath parent() { checkRootAccess(); - return new ZPathImpl(nodes.subList(0, nodes.size() - 1)); + return new ZPathImpl(nodes.subList(0, nodes.size() - 1), null); } @Override @@ -67,8 +70,15 @@ public class ZPathImpl implements ZPath } @Override + public Pattern toSchemaPathPattern() + { + return Pattern.compile(fullPath() + ZKPaths.PATH_SEPARATOR + ".*"); + } + + @Override public String fullPath() { + checkResolved(); return buildFullPath(false); } @@ -76,12 +86,14 @@ public class ZPathImpl implements ZPath public String parentPath() { checkRootAccess(); + checkResolved(); return buildFullPath(true); } @Override public String nodeName() { + checkResolved(); return nodes.get(nodes.size() - 1); } @@ -114,18 +126,39 @@ public class ZPathImpl implements ZPath return "ZPathImpl{" + "nodes=" + nodes + '}'; } - private ZPathImpl(List<String> nodes) + @Override + public ZPath resolved(Object... parameters) { - this.nodes = Objects.requireNonNull(nodes, "nodes cannot be null"); + List<String> nodeNames = Lists.newArrayList(); + for ( int i = 0; i < nodes.size(); ++i ) + { + String name = nodes.get(i); + if ( name.equals(parameter) ) + { + if ( parameters.length >= i ) + { + throw new IllegalStateException(String.format("Parameter missing at index [%d] for [%s]", i, nodes.toString())); + } + nodeNames.add(parameters[i].toString()); + } + else + { + nodeNames.add(name); + } + } + return new ZPathImpl(nodeNames, null); } private ZPathImpl(List<String> nodes, String child) { - PathUtils.validatePath(ZKPaths.PATH_SEPARATOR + child); - this.nodes = ImmutableList.<String>builder() - .addAll(nodes) - .add(child) - .build(); + ImmutableList.Builder<String> builder = ImmutableList.<String>builder().addAll(nodes); + if ( child != null ) + { + validate(child); + builder.add(child); + } + this.nodes = builder.build(); + isResolved = !this.nodes.contains(parameter); } private void checkRootAccess() @@ -136,14 +169,13 @@ public class ZPathImpl implements ZPath } } - private String buildFullPath(boolean parent) + private void checkResolved() { - String path = parent ? parentPathCache : fullPathCache; - if ( path != null ) - { - return path; - } + Preconditions.checkState(isResolved, "This ZPath has not been resolved"); + } + private String buildFullPath(boolean parent) + { boolean addSeparator = false; StringBuilder str = new StringBuilder(); int size = parent ? (nodes.size() - 1) : nodes.size(); @@ -153,18 +185,22 @@ public class ZPathImpl implements ZPath { str.append(ZKPaths.PATH_SEPARATOR); } - str.append(nodes.get(i)); + String value = nodes.get(i); + str.append(value.equals(parameter) ? ".*" : value); } - path = str.toString(); + return str.toString(); + } - if ( parent ) + private static void validate(String nodeName) + { + if ( parameter.equals(Objects.requireNonNull(nodeName, "nodeName cannot be null")) ) { - parentPathCache = path; + return; } - else + if ( nodeName.equals(ZKPaths.PATH_SEPARATOR) ) { - fullPathCache = path; + return; } - return path; + PathUtils.validatePath(ZKPaths.PATH_SEPARATOR + nodeName); } }
