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 2016/05/19 19:02:25 UTC

[10/35] curator git commit: more doc, some testing, schema set loader

more doc, some testing, schema set loader


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

Branch: refs/heads/CURATOR-3.0
Commit: 734f4533808f1a4b8b8086407476bca8862339ff
Parents: 8ccfdeb
Author: randgalt <ra...@apache.org>
Authored: Mon May 2 21:41:25 2016 -0500
Committer: randgalt <ra...@apache.org>
Committed: Mon May 2 21:41:25 2016 -0500

----------------------------------------------------------------------
 curator-framework/pom.xml                       |  12 ++
 .../curator/framework/schema/SchemaSet.java     |  34 +++--
 .../framework/schema/SchemaSetLoader.java       | 148 +++++++++++++++++++
 .../framework/schema/SchemaViolation.java       |   7 +
 .../curator/framework/imps/TestSchema.java      |  61 ++++++++
 .../src/test/resources/schema1.json             |   9 ++
 pom.xml                                         |  13 ++
 7 files changed, 272 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/pom.xml
----------------------------------------------------------------------
diff --git a/curator-framework/pom.xml b/curator-framework/pom.xml
index 32b2cdb..c47e265 100644
--- a/curator-framework/pom.xml
+++ b/curator-framework/pom.xml
@@ -57,6 +57,18 @@
         </dependency>
 
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSet.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSet.java b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSet.java
index 1014dc8..768f6c6 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSet.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSet.java
@@ -41,6 +41,7 @@ public class SchemaSet
         .build(cacheLoader);
 
     private static final Schema defaultSchema = new Schema(null, "", "Default schema", new DefaultDataValidator(), Schema.Allowance.CAN, Schema.Allowance.CAN, Schema.Allowance.CAN, true);
+    private final boolean useDefaultSchema;
 
     /**
      * Return the default (empty) schema set
@@ -49,7 +50,7 @@ public class SchemaSet
      */
     public static SchemaSet getDefaultSchemaSet()
     {
-        return new SchemaSet(Collections.<SchemaKey, Schema>emptyMap())
+        return new SchemaSet(Collections.<SchemaKey, Schema>emptyMap(), true)
         {
             @Override
             public String toDocumentation()
@@ -62,9 +63,11 @@ public class SchemaSet
     /**
      * @param schemas the schemas for the set. The key of the map is a key/name for the schema that can be
      *                used when calling {@link #getNamedSchema(SchemaKey)}
+     * @param useDefaultSchema if true, return a default schema when there is no match. Otherwise, an exception is thrown
      */
-    public SchemaSet(Map<SchemaKey, Schema> schemas)
+    public SchemaSet(Map<SchemaKey, Schema> schemas, boolean useDefaultSchema)
     {
+        this.useDefaultSchema = useDefaultSchema;
         this.schemas = ImmutableMap.copyOf(Preconditions.checkNotNull(schemas, "schemas cannot be null"));
         ImmutableMap.Builder<String, Schema> builder = ImmutableMap.builder();
         for ( Schema schema : schemas.values() )
@@ -85,24 +88,31 @@ public class SchemaSet
      */
     public Schema getSchema(String path)
     {
-        if ( schemas.size() == 0 )
+        Schema schema = null;
+        if ( schemas.size() > 0 )
         {
-            return defaultSchema;
+            schema = pathSchemas.get(path);
+            if ( schema == null )
+            {
+                try
+                {
+                    schema = regexCache.get(path);
+                }
+                catch ( ExecutionException e )
+                {
+                    throw new RuntimeException(e);
+                }
+            }
         }
-        Schema schema = pathSchemas.get(path);
         if ( schema != null )
         {
             return schema;
         }
-
-        try
+        if ( useDefaultSchema )
         {
-            return regexCache.get(path);
-        }
-        catch ( ExecutionException e )
-        {
-            throw new RuntimeException(e);
+            return defaultSchema;
         }
+        throw new SchemaViolation("No schema found for: " + path);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSetLoader.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSetLoader.java b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSetLoader.java
new file mode 100644
index 0000000..092b161
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaSetLoader.java
@@ -0,0 +1,148 @@
+package org.apache.curator.framework.schema;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ *     Utility to load schems set from a JSON stream/file. <strong>NOTE:</strong>
+ *     to avoid adding a new dependency to Curator, the Jackson library has been used
+ *     with "provided" scope. You will need to add a dependency to <code>com.fasterxml.jackson.core:jackson-core:2.7.3</code>
+ *     and <code>com.fasterxml.jackson.core:jackson-databind:2.7.3</code> to your project
+ * </p>
+ *
+ * <p>
+ *     The JSON stream should be an array of named schemas:<br><br>
+ * <code><pre>
+ * [
+ *     {
+ *         "name": "name",                       required - name of the schema
+ *         "path": "path or pattern",            required - full path or regex pattern
+ *         "isRegex": true/false,                optional - true if path is a regular expression - default is false
+ *         "dataValidator": "name",              optional - name of a data validator - default is no validator
+ *         "documentation": "docs",              optional - user displayable docs - default is ""
+ *         "ephemeral": "allowance",             optional - "can", "must" or "cannot" - default is "can"
+ *         "sequential": "allowance",            optional - "can", "must" or "cannot" - default is "can"
+ *         "watched": "allowance",               optional - "can", "must" or "cannot" - default is "can"
+ *         "canBeDeleted": "true/false           optional - true if ZNode at path can be deleted - default is true
+ *     }
+ * ]
+ * </pre></code>
+ * </p>
+ */
+public class SchemaSetLoader
+{
+    private final Map<SchemaKey, Schema> schemas;
+
+    /**
+     * Called to map a data validator name in the JSON stream to an actual data validator
+     */
+    public interface DataValidatorMapper
+    {
+        /**
+         * @param name name of the validator
+         * @return the validator
+         */
+        DataValidator getDataValidator(String name);
+    }
+
+    public SchemaSetLoader(String json, DataValidatorMapper dataValidatorMapper)
+    {
+        this(new StringReader(json), dataValidatorMapper);
+    }
+
+    public SchemaSetLoader(Reader in, DataValidatorMapper dataValidatorMapper)
+    {
+        ImmutableMap.Builder<SchemaKey, Schema> builder = ImmutableMap.builder();
+        try
+        {
+            JsonNode root = new ObjectMapper().readTree(in);
+            read(builder, root, dataValidatorMapper);
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException(e);
+        }
+        schemas = builder.build();
+    }
+
+    public SchemaSet toSchemaSet(boolean useDefaultSchema)
+    {
+        return new SchemaSet(schemas, useDefaultSchema);
+    }
+
+    private void read(ImmutableMap.Builder<SchemaKey, Schema> builder, JsonNode node, DataValidatorMapper dataValidatorMapper)
+    {
+        for ( JsonNode child : node )
+        {
+            readNode(builder, child, dataValidatorMapper);
+        }
+    }
+
+    private void readNode(ImmutableMap.Builder<SchemaKey, Schema> builder, JsonNode node, DataValidatorMapper dataValidatorMapper)
+    {
+        String name = getText(node, "name", null);
+        String path = getText(node, "path", null);
+        boolean isRegex = getBoolean(node, "isRegex");
+        if ( name == null )
+        {
+            throw new RuntimeException("name is required at: " + node);
+        }
+        if ( path == null )
+        {
+            throw new RuntimeException("path is required at: " + node);
+        }
+
+        SchemaBuilder schemaBuilder = isRegex ? Schema.builder(Pattern.compile(path)) : Schema.builder(path);
+
+        String dataValidatorName = getText(node, "dataValidator", null);
+        if ( dataValidatorName != null )
+        {
+            if ( dataValidatorMapper == null )
+            {
+                throw new RuntimeException("No DataValidatorMapper provided but needed at: " + node);
+            }
+            schemaBuilder.dataValidator(dataValidatorMapper.getDataValidator(dataValidatorName));
+        }
+
+        Schema schema = schemaBuilder.documentation(getText(node, "documentation", ""))
+            .ephemeral(getAllowance(node, "ephemeral"))
+            .sequential(getAllowance(node, "sequential"))
+            .watched(getAllowance(node, "watched"))
+            .canBeDeleted(getBoolean(node, "canBeDeleted"))
+            .build();
+        builder.put(new SchemaKey(name), schema);
+    }
+
+    private String getText(JsonNode node, String name, String defaultValue)
+    {
+        JsonNode namedNode = node.get(name);
+        return (namedNode != null) ? namedNode.asText() : defaultValue;
+    }
+
+    private boolean getBoolean(JsonNode node, String name)
+    {
+        JsonNode namedNode = node.get(name);
+        return (namedNode != null) && namedNode.asBoolean();
+    }
+
+    private Schema.Allowance getAllowance(JsonNode node, String name)
+    {
+        JsonNode namedNode = node.get(name);
+        try
+        {
+            return (namedNode != null) ? Schema.Allowance.valueOf(namedNode.asText().toUpperCase()) : Schema.Allowance.CAN;
+        }
+        catch ( IllegalArgumentException ignore )
+        {
+            throw new RuntimeException("Must be one of: " + Arrays.toString(Schema.Allowance.values()) + " at " + node);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaViolation.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaViolation.java b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaViolation.java
index afd6bbf..4953c65 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaViolation.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/schema/SchemaViolation.java
@@ -8,6 +8,13 @@ public class SchemaViolation extends RuntimeException
     private final Schema schema;
     private final String violation;
 
+    public SchemaViolation(String violation)
+    {
+        super(String.format("Schema violation: %s", violation));
+        this.schema = null;
+        this.violation = violation;
+    }
+
     public SchemaViolation(Schema schema, String violation)
     {
         super(String.format("Schema violation: %s for schema: %s", violation, schema));

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/src/test/java/org/apache/curator/framework/imps/TestSchema.java
----------------------------------------------------------------------
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestSchema.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestSchema.java
new file mode 100644
index 0000000..a55ad8e
--- /dev/null
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestSchema.java
@@ -0,0 +1,61 @@
+package org.apache.curator.framework.imps;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.schema.SchemaSet;
+import org.apache.curator.framework.schema.SchemaSetLoader;
+import org.apache.curator.framework.schema.SchemaViolation;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.zookeeper.CreateMode;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.io.IOException;
+
+public class TestSchema extends BaseClassForTests
+{
+    @Test
+    public void testBasics() throws Exception
+    {
+        SchemaSet schemaSet = loadSchemaSet("schema1.json");
+        CuratorFramework client = newClient(schemaSet);
+        try
+        {
+            client.start();
+
+            try
+            {
+                client.create().creatingParentsIfNeeded().forPath("/a/b/c");
+                Assert.fail("Should've violated schema");
+            }
+            catch ( SchemaViolation dummy )
+            {
+                // expected
+            }
+
+            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/a/b/c");
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    private CuratorFramework newClient(SchemaSet schemaSet)
+    {
+        return CuratorFrameworkFactory.builder()
+            .connectString(server.getConnectString())
+            .retryPolicy(new RetryOneTime(1))
+            .schemaSet(schemaSet)
+            .build();
+    }
+
+    private SchemaSet loadSchemaSet(String name) throws IOException
+    {
+        String json = Resources.toString(Resources.getResource(name), Charsets.UTF_8);
+        return new SchemaSetLoader(json, null).toSchemaSet(true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/curator-framework/src/test/resources/schema1.json
----------------------------------------------------------------------
diff --git a/curator-framework/src/test/resources/schema1.json b/curator-framework/src/test/resources/schema1.json
new file mode 100644
index 0000000..5491059
--- /dev/null
+++ b/curator-framework/src/test/resources/schema1.json
@@ -0,0 +1,9 @@
+[
+  {
+    "name": "test",
+    "path": "/a/b/c",
+    "documentation": "This is a schema",
+    "ephemeral": "must",
+    "sequential": "cannot"
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/curator/blob/734f4533/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f946f59..d9d33fa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
         <maven-license-plugin-version>1.9.0</maven-license-plugin-version>
         <commons-math-version>2.2</commons-math-version>
         <jackson-mapper-asl-version>1.9.13</jackson-mapper-asl-version>
+        <jackson-version>2.7.3</jackson-version>
         <jersey-version>1.18.1</jersey-version>
         <jsr311-api-version>1.1.1</jsr311-api-version>
         <jetty-version>6.1.26</jetty-version>
@@ -356,6 +357,18 @@
             </dependency>
 
             <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-core</artifactId>
+                <version>${jackson-version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>${jackson-version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>com.sun.jersey</groupId>
                 <artifactId>jersey-server</artifactId>
                 <version>${jersey-version}</version>