Repository: johnzon
Updated Branches:
  refs/heads/master 5f5b40906 -> 947c91b8a


JOHNZON-171 basic jsonschema validation module - not yet complete


Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/947c91b8
Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/947c91b8
Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/947c91b8

Branch: refs/heads/master
Commit: 947c91b8a6b17d87b464a8a87423ea7b577d27f7
Parents: 5f5b409
Author: Romain Manni-Bucau <[email protected]>
Authored: Sun Apr 29 20:09:40 2018 +0200
Committer: Romain Manni-Bucau <[email protected]>
Committed: Sun Apr 29 20:09:40 2018 +0200

----------------------------------------------------------------------
 johnzon-jsonschema/pom.xml                      |  43 +++
 .../johnzon/jsonschema/JsonSchemaValidator.java |  59 ++++
 .../jsonschema/JsonSchemaValidatorFactory.java  | 156 +++++++++
 .../johnzon/jsonschema/ValidationResult.java    |  90 +++++
 .../jsonschema/spi/ValidationContext.java       |  61 ++++
 .../jsonschema/spi/ValidationExtension.java     |  31 ++
 .../spi/builtin/BaseNumberValidationImpl.java   |  70 ++++
 .../jsonschema/spi/builtin/EnumValidation.java  |  78 +++++
 .../spi/builtin/ExclusiveMaximumValidation.java |  67 ++++
 .../spi/builtin/ExclusiveMinimumValidation.java |  67 ++++
 .../spi/builtin/MaxLengthValidation.java        |  73 ++++
 .../spi/builtin/MaximumValidation.java          |  67 ++++
 .../spi/builtin/MinLengthValidation.java        |  73 ++++
 .../spi/builtin/MinimumValidation.java          |  67 ++++
 .../spi/builtin/MultipleOfValidation.java       |  68 ++++
 .../spi/builtin/PatternValidation.java          | 125 +++++++
 .../spi/builtin/RequiredValidation.java         |  86 +++++
 .../jsonschema/spi/builtin/TypeValidation.java  |  86 +++++
 .../jsonschema/JsonSchemaValidatorTest.java     | 351 +++++++++++++++++++
 pom.xml                                         |   1 +
 20 files changed, 1719 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/pom.xml
----------------------------------------------------------------------
diff --git a/johnzon-jsonschema/pom.xml b/johnzon-jsonschema/pom.xml
new file mode 100644
index 0000000..988941e
--- /dev/null
+++ b/johnzon-jsonschema/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="
+          http://maven.apache.org/POM/4.0.0
+          http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <parent>
+    <artifactId>johnzon</artifactId>
+    <groupId>org.apache.johnzon</groupId>
+    <version>1.1.8-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>johnzon-jsonschema</artifactId>
+  <name>Johnzon :: JSON Schema</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.johnzon</groupId>
+      <artifactId>johnzon-core</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java
new file mode 100644
index 0000000..f2f2372
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.johnzon.jsonschema;
+
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toList;
+
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+public class JsonSchemaValidator implements Function<JsonObject, 
ValidationResult>, AutoCloseable {
+    private static final ValidationResult SUCCESS = new 
ValidationResult(emptyList());
+    private final Function<JsonValue, 
Stream<ValidationResult.ValidationError>> validationFunction;
+
+    JsonSchemaValidator(final Function<JsonValue, 
Stream<ValidationResult.ValidationError>> validationFunction) {
+        this.validationFunction = validationFunction;
+    }
+
+    @Override
+    public ValidationResult apply(final JsonObject object) {
+        final Collection<ValidationResult.ValidationError> errors = 
validationFunction.apply(object).collect(toList());
+        if (!errors.isEmpty()) {
+            return new ValidationResult(errors);
+        }
+        return SUCCESS;
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public String toString() {
+        return "JsonSchemaValidator{" +
+                "validationFunction=" + validationFunction +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java
new file mode 100644
index 0000000..b409406
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java
@@ -0,0 +1,156 @@
+/*
+ * 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.johnzon.jsonschema;
+
+import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+import org.apache.johnzon.jsonschema.spi.builtin.EnumValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.ExclusiveMaximumValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.ExclusiveMinimumValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.MaxLengthValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.MaximumValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.MinLengthValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.MinimumValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.MultipleOfValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.PatternValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.RequiredValidation;
+import org.apache.johnzon.jsonschema.spi.builtin.TypeValidation;
+
+public class JsonSchemaValidatorFactory implements AutoCloseable {
+    private static final String[] ROOT_PATH = new String[0];
+    private static final Function<JsonValue, 
Stream<ValidationResult.ValidationError>> NO_VALIDATION = new 
Function<JsonValue, Stream<ValidationResult.ValidationError>>() {
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(JsonValue 
jsonValue) {
+            return Stream.empty();
+        }
+
+        @Override
+        public String toString() {
+            return "NoValidation";
+        }
+    };
+
+    private final List<ValidationExtension> extensions = new ArrayList<>();
+
+    public JsonSchemaValidatorFactory() {
+        extensions.addAll(asList(
+                new RequiredValidation(),
+                new TypeValidation(),
+                new EnumValidation(),
+                new MultipleOfValidation(),
+                new MaximumValidation(),
+                new MinimumValidation(),
+                new ExclusiveMaximumValidation(),
+                new ExclusiveMinimumValidation(),
+                new MaxLengthValidation(),
+                new MinLengthValidation(),
+                new PatternValidation()
+                // todo: 
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4 and 
following
+        ));
+        extensions.addAll(new 
ArrayList<>(StreamSupport.stream(ServiceLoader.load(ValidationExtension.class).spliterator(),
 false)
+                .collect(toList())));
+    }
+
+    public JsonSchemaValidatorFactory appendExtensions(final 
ValidationExtension... extensions) {
+        this.extensions.addAll(asList(extensions));
+        return this;
+    }
+
+    public JsonSchemaValidatorFactory setExtensions(final 
ValidationExtension... extensions) {
+        this.extensions.clear();
+        return appendExtensions(extensions);
+    }
+
+    public JsonSchemaValidator newInstance(final JsonObject schema) {
+        return new JsonSchemaValidator(buildValidator(ROOT_PATH, schema));
+    }
+
+    @Override
+    public void close() {
+        // no-op for now
+    }
+
+    private Function<JsonValue, Stream<ValidationResult.ValidationError>> 
buildValidator(final String[] path,
+                                                                               
          final JsonObject schema) {
+        final List<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> directValidations = 
buildDirectValidations(path, schema).collect(toList());
+        final Function<JsonValue, Stream<ValidationResult.ValidationError>> 
nestedValidations = buildNestedValidations(path, schema);
+        return new 
ValidationsFunction(Stream.concat(directValidations.stream(), 
Stream.of(nestedValidations)).collect(toList()));
+    }
+
+    private Stream<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> buildDirectValidations(final 
String[] path, final JsonObject schema) {
+        final ValidationContext model = new ValidationContext(path, schema);
+        return extensions.stream().map(e -> 
e.create(model)).filter(Optional::isPresent).map(Optional::get);
+    }
+
+    private Function<JsonValue, Stream<ValidationResult.ValidationError>> 
buildNestedValidations(final String[] path, final JsonObject schema) {
+        return ofNullable(schema.get("properties"))
+                .filter(it -> it.getValueType() == JsonValue.ValueType.OBJECT)
+                .map(it -> it.asJsonObject().entrySet().stream()
+                        .filter(obj -> obj.getValue().getValueType() == 
JsonValue.ValueType.OBJECT)
+                        .map(obj -> {
+                            final String[] fieldPath = 
Stream.concat(Stream.of(path), Stream.of(obj.getKey())).toArray(String[]::new);
+                            return buildValidator(fieldPath, 
obj.getValue().asJsonObject());
+                        })
+                        .collect(toList()))
+                .map(this::toFunction)
+                .orElse(NO_VALIDATION);
+    }
+
+    private Function<JsonValue, Stream<ValidationResult.ValidationError>> 
toFunction(
+            final List<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> validations) {
+        return new ValidationsFunction(validations);
+    }
+
+    private static class ValidationsFunction implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+        private final List<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> delegates;
+
+        private ValidationsFunction(final List<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> validations) {
+            // unwrap when possible to simplify the stack and make toString 
readable (debug)
+            this.delegates = validations.stream()
+                    .flatMap(it -> ValidationsFunction.class.isInstance(it) ? 
ValidationsFunction.class.cast(it).delegates.stream() : Stream.of(it))
+                    .filter(it -> it != NO_VALIDATION)
+                    .collect(toList());
+        }
+
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(final JsonValue 
jsonValue) {
+            return delegates.stream().flatMap(v -> v.apply(jsonValue));
+        }
+
+        @Override
+        public String toString() {
+            return delegates.toString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/ValidationResult.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/ValidationResult.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/ValidationResult.java
new file mode 100644
index 0000000..6d574c9
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/ValidationResult.java
@@ -0,0 +1,90 @@
+/*
+ * 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.johnzon.jsonschema;
+
+import java.util.Collection;
+
+public class ValidationResult {
+    private Collection<ValidationError> errors;
+
+    public ValidationResult() {
+        // no-op
+    }
+
+    ValidationResult(final Collection<ValidationError> errors) {
+        this.errors = errors;
+    }
+
+    public boolean isSuccess() {
+        return errors == null || errors.isEmpty();
+    }
+
+    public Collection<ValidationError> getErrors() {
+        return errors;
+    }
+
+    public void setErrors(final Collection<ValidationError> errors) {
+        this.errors = errors;
+    }
+
+    @Override
+    public String toString() {
+        return "ValidationResult{" +
+                "errors=" + errors +
+                '}';
+    }
+
+    public static class ValidationError {
+        private String field;
+        private String message;
+
+        public ValidationError() {
+            // no-op
+        }
+
+        public ValidationError(final String field, final String message) {
+            this.field = field;
+            this.message = message;
+        }
+
+        public String getField() {
+            return field;
+        }
+
+        public void setField(final String field) {
+            this.field = field;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public void setMessage(final String message) {
+            this.message = message;
+        }
+
+        @Override
+        public String toString() {
+            return "ValidationError{" +
+                    "field='" + field + '\'' +
+                    ", message='" + message + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java
new file mode 100644
index 0000000..63535a5
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java
@@ -0,0 +1,61 @@
+/*
+ * 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.johnzon.jsonschema.spi;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+public class ValidationContext {
+    private final String[] path;
+    private final JsonObject schema;
+
+    public ValidationContext(final String[] path, final JsonObject schema) {
+        this.path = path;
+        this.schema = schema;
+    }
+
+    public String[] getPath() {
+        return path;
+    }
+
+    public JsonObject getSchema() {
+        return schema;
+    }
+
+    public JsonValue readValue(final JsonValue root) { // move to JsonPointer? 
requires to store a provider if we want
+        JsonValue current = root;
+        for (final String segment : path) {
+            if (current == null) {
+                return null;
+            }
+            if (current.getValueType() == JsonValue.ValueType.OBJECT) {
+                current = current.asJsonObject().get(segment);
+            }
+        }
+        return current;
+    }
+
+    public String toPointer() {
+        return Stream.of(path).collect(joining("/", "/", ""));
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationExtension.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationExtension.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationExtension.java
new file mode 100644
index 0000000..0b9b22c
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationExtension.java
@@ -0,0 +1,31 @@
+/*
+ * 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.johnzon.jsonschema.spi;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+
+public interface ValidationExtension {
+    Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> 
create(ValidationContext model);
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java
new file mode 100644
index 0000000..e98e998
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+
+abstract class BaseNumberValidationImpl implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+    protected final String pointer;
+    protected final Function<JsonObject, JsonValue> extractor;
+    protected final double bound;
+    private final JsonValue.ValueType validType;
+
+    BaseNumberValidationImpl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound,
+                             final JsonValue.ValueType validType) {
+        this.bound = bound;
+        this.pointer = pointer;
+        this.extractor = extractor;
+        this.validType = validType;
+    }
+
+    @Override
+    public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) 
{
+        if (obj == null || obj == JsonValue.NULL) {
+            return Stream.empty();
+        }
+        final JsonValue value = extractor.apply(obj.asJsonObject());
+        if (value == null || value.getValueType() != validType) {
+            return Stream.empty();
+        }
+        final double val = toNumber(value);
+        if (val <= 0) {
+            return toError(val);
+        }
+        if (isValid(val)) {
+            return Stream.empty();
+        }
+        return toError(val);
+    }
+
+    protected double toNumber(final JsonValue value) {
+        return JsonNumber.class.cast(value).doubleValue();
+    }
+
+    protected abstract boolean isValid(double val);
+
+    protected abstract Stream<ValidationResult.ValidationError> toError(double 
val);
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java
new file mode 100644
index 0000000..3cacbed
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java
@@ -0,0 +1,78 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import static java.util.Optional.ofNullable;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class EnumValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        return ofNullable(model.getSchema().get("enum"))
+                .filter(it -> it.getValueType() == JsonValue.ValueType.ARRAY)
+                .map(JsonValue::asJsonArray)
+                .map(values -> new Impl(values, model.toPointer(), 
model::readValue));
+    }
+
+    private static class Impl implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+        private final Collection<JsonValue> valid;
+        private final String pointer;
+        private final Function<JsonObject, JsonValue> extractor;
+
+        private Impl(final Collection<JsonValue> valid, final String pointer, 
final Function<JsonObject, JsonValue> extractor) {
+            this.valid = valid;
+            this.pointer = pointer;
+            this.extractor = extractor;
+        }
+
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(final JsonValue 
obj) {
+            if (obj == null || obj == JsonValue.NULL) {
+                return Stream.empty();
+            }
+            final JsonValue value = extractor.apply(obj.asJsonObject());
+            if (value != null && !JsonValue.NULL.equals(value)) {
+                return Stream.empty();
+            }
+            if (valid.contains(value)) {
+                return Stream.empty();
+            }
+            return Stream.of(new ValidationResult.ValidationError(pointer, 
"Invalid value, got " + value + ", expected: " + valid));
+        }
+
+        @Override
+        public String toString() {
+            return "Enum{" +
+                    "valid=" + valid +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java
new file mode 100644
index 0000000..2e06140
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class ExclusiveMaximumValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("number")) {
+            return 
Optional.ofNullable(model.getSchema().get("exclusiveMaximum"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).doubleValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.NUMBER);
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val < this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " is strictly more than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "ExclusiveMaximum{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java
new file mode 100644
index 0000000..a649476
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class ExclusiveMinimumValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("number")) {
+            return 
Optional.ofNullable(model.getSchema().get("exclusiveMinimum"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).doubleValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.NUMBER);
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val > this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " is strictly less than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "ExclusiveMinimum{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java
new file mode 100644
index 0000000..2049dbb
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java
@@ -0,0 +1,73 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class MaxLengthValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("string")) {
+            return Optional.ofNullable(model.getSchema().get("maxLength"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).intValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.STRING);
+        }
+
+        @Override
+        protected double toNumber(final JsonValue value) {
+            return JsonString.class.cast(value).getString().length();
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val <= this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " length is more than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "MaxLength{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java
new file mode 100644
index 0000000..f0a6160
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class MaximumValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("number")) {
+            return Optional.ofNullable(model.getSchema().get("maximum"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).doubleValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.NUMBER);
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val <= this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " is more than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "Maximum{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java
new file mode 100644
index 0000000..54449a3
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java
@@ -0,0 +1,73 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class MinLengthValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("string")) {
+            return Optional.ofNullable(model.getSchema().get("minLength"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).intValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.STRING);
+        }
+
+        @Override
+        protected double toNumber(final JsonValue value) {
+            return JsonString.class.cast(value).getString().length();
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val >= this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " length is less than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "MinLength{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java
new file mode 100644
index 0000000..1207111
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class MinimumValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("number")) {
+            return Optional.ofNullable(model.getSchema().get("minimum"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).doubleValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double bound) {
+            super(pointer, extractor, bound, JsonValue.ValueType.NUMBER);
+        }
+
+        @Override
+        protected boolean isValid(final double val) {
+            return val >= this.bound;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " is less than " + this.bound));
+        }
+
+        @Override
+        public String toString() {
+            return "Minimum{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java
new file mode 100644
index 0000000..951dca8
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.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 org.apache.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class MultipleOfValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("number")) {
+            return Optional.ofNullable(model.getSchema().get("multipleOf"))
+                    .filter(v -> v.getValueType() == 
JsonValue.ValueType.NUMBER)
+                    .map(m -> new Impl(model.toPointer(), model::readValue, 
JsonNumber.class.cast(m).doubleValue()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl extends BaseNumberValidationImpl {
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final double multipleOf) {
+            super(pointer, extractor, multipleOf, JsonValue.ValueType.NUMBER);
+        }
+
+        @Override
+        protected boolean isValid(double val) {
+            final double divided = val / bound;
+            return divided == (long) divided;
+        }
+
+        @Override
+        protected Stream<ValidationResult.ValidationError> toError(final 
double val) {
+            return Stream.of(new ValidationResult.ValidationError(pointer, val 
+ " is not a multiple of " + bound));
+        }
+
+        @Override
+        public String toString() {
+            return "MultipleOf{" +
+                    "factor=" + bound +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java
new file mode 100644
index 0000000..4b4d284
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java
@@ -0,0 +1,125 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class PatternValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        if (model.getSchema().getString("type", "object").equals("string")) {
+            return Optional.ofNullable(model.getSchema().get("pattern"))
+                    .filter(val -> val.getValueType() == 
JsonValue.ValueType.STRING)
+                    .map(pattern -> new Impl(model.toPointer(), 
model::readValue, JsonString.class.cast(pattern).getString()));
+        }
+        return Optional.empty();
+    }
+
+    private static class Impl implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+        private final String pointer;
+        private final Function<JsonObject, JsonValue> extractor;
+        private final JsRegex jsRegex;
+
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final String pattern) {
+            this.jsRegex = new JsRegex(pattern);
+            this.pointer = pointer;
+            this.extractor = extractor;
+        }
+
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(final JsonValue 
obj) {
+            if (obj == null || obj == JsonValue.NULL) {
+                return Stream.empty();
+            }
+            final JsonValue value = extractor.apply(obj.asJsonObject());
+            if (value == null || value.getValueType() != 
JsonValue.ValueType.STRING || JsonValue.NULL.equals(value)) {
+                return Stream.empty();
+            }
+            if (!jsRegex.test(JsonString.class.cast(value).getString())) {
+                return Stream.of(new ValidationResult.ValidationError(pointer, 
value + " doesn't match " + jsRegex));
+            }
+            return Stream.empty();
+        }
+
+        @Override
+        public String toString() {
+            return "Pattern{" +
+                    "regex=" + jsRegex +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+
+    private static class JsRegex implements Predicate<CharSequence> {
+
+        private static final ScriptEngine ENGINE;
+
+        static {
+            ENGINE = new ScriptEngineManager().getEngineByName("javascript");
+        }
+
+        private final String regex;
+
+        private final String indicators;
+
+        private JsRegex(final String regex) {
+            if (regex.startsWith("/") && regex.length() > 1) {
+                final int end = regex.lastIndexOf('/');
+                if (end < 0) {
+                    this.regex = regex;
+                    this.indicators = "";
+                } else {
+                    this.regex = regex.substring(1, end);
+                    this.indicators = regex.substring(end + 1);
+                }
+            } else {
+                this.regex = regex;
+                this.indicators = "";
+            }
+        }
+
+        @Override
+        public boolean test(final CharSequence string) {
+            final Bindings bindings = ENGINE.createBindings();
+            bindings.put("text", string);
+            bindings.put("regex", regex);
+            bindings.put("indicators", indicators);
+            try {
+                return Boolean.class.cast(ENGINE.eval("new RegExp(regex, 
indicators).test(text)", bindings));
+            } catch (final ScriptException e) {
+                return false;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java
new file mode 100644
index 0000000..cc547b1
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java
@@ -0,0 +1,86 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toSet;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class RequiredValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        return ofNullable(model.getSchema().get("required"))
+                .filter(it -> it.getValueType() == JsonValue.ValueType.ARRAY)
+                .map(JsonValue::asJsonArray)
+                .filter(arr -> arr.stream().allMatch(it -> it.getValueType() 
== JsonValue.ValueType.STRING))
+                .map(arr -> arr.stream().map(it -> 
JsonString.class.cast(it).getString()).collect(toSet()))
+                .map(required -> new Impl(required, model.toPointer(), 
model::readValue));
+    }
+
+    private static class Impl implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+        private final Collection<String> required;
+        private final String pointer;
+        private final Function<JsonObject, JsonValue> extractor;
+
+        private Impl(final Collection<String> required, final String pointer, 
final Function<JsonObject, JsonValue> extractor) {
+            this.required = required;
+            this.pointer = pointer;
+            this.extractor = extractor;
+        }
+
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(final JsonValue 
obj) {
+            if (obj == null || obj == JsonValue.NULL) {
+                return toErrors(required.stream());
+            }
+            return toErrors(required.stream().filter(name -> {
+                final JsonValue jsonValue = 
extractor.apply(obj.asJsonObject());
+                return isNull(jsonValue) || 
isNull(jsonValue.asJsonObject().get(name));
+            }));
+        }
+
+        private boolean isNull(final JsonValue jsonValue) {
+            return jsonValue == null || JsonValue.NULL.equals(jsonValue);
+        }
+
+        private Stream<ValidationResult.ValidationError> toErrors(final 
Stream<String> fields) {
+            return fields.map(name -> new 
ValidationResult.ValidationError(pointer, name + " is required and is not 
present"));
+        }
+
+        @Override
+        public String toString() {
+            return "Required{" +
+                    "required=" + required +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java
 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java
new file mode 100644
index 0000000..35122aa
--- /dev/null
+++ 
b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java
@@ -0,0 +1,86 @@
+/*
+ * 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.johnzon.jsonschema.spi.builtin;
+
+import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.joining;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+import org.apache.johnzon.jsonschema.ValidationResult;
+import org.apache.johnzon.jsonschema.spi.ValidationContext;
+import org.apache.johnzon.jsonschema.spi.ValidationExtension;
+
+public class TypeValidation implements ValidationExtension {
+    @Override
+    public Optional<Function<JsonValue, 
Stream<ValidationResult.ValidationError>>> create(final ValidationContext 
model) {
+        switch (model.getSchema().getString("type", "object")) {
+            case "string":
+                return Optional.of(new Impl(model.toPointer(), 
model::readValue, JsonValue.ValueType.STRING));
+            case "number":
+                return Optional.of(new Impl(model.toPointer(), 
model::readValue, JsonValue.ValueType.NUMBER));
+            case "array":
+                return Optional.of(new Impl(model.toPointer(), 
model::readValue, JsonValue.ValueType.ARRAY));
+            case "boolean":
+                return Optional.of(new Impl(model.toPointer(), 
model::readValue, JsonValue.ValueType.FALSE, JsonValue.ValueType.TRUE));
+            case "object":
+            default:
+                return Optional.of(new Impl(model.toPointer(), 
model::readValue, JsonValue.ValueType.OBJECT));
+        }
+    }
+
+    private static class Impl implements Function<JsonValue, 
Stream<ValidationResult.ValidationError>> {
+        private final String pointer;
+        private final Function<JsonObject, JsonValue> extractor;
+        private final JsonValue.ValueType[] types;
+
+        private Impl(final String pointer, final Function<JsonObject, 
JsonValue> extractor, final JsonValue.ValueType... types) {
+            this.types = types;
+            this.pointer = pointer;
+            this.extractor = extractor;
+        }
+
+        @Override
+        public Stream<ValidationResult.ValidationError> apply(final JsonValue 
obj) {
+            if (obj == null || obj == JsonValue.NULL) {
+                return Stream.empty();
+            }
+            final JsonValue value = extractor.apply(obj.asJsonObject());
+            if (value == null || Stream.of(types).anyMatch(it -> it == 
value.getValueType()) || JsonValue.ValueType.NULL == value.getValueType()) {
+                return Stream.empty();
+            }
+            return Stream.of(new ValidationResult.ValidationError(
+                    pointer,
+                    "Expected " + 
Stream.of(types).map(JsonValue.ValueType::name).collect(joining(", ")) + " but 
got " + value.getValueType()));
+        }
+
+        @Override
+        public String toString() {
+            return "Type{" +
+                    "type=" + asList(types) +
+                    ", pointer='" + pointer + '\'' +
+                    '}';
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/johnzon-jsonschema/src/test/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorTest.java
----------------------------------------------------------------------
diff --git 
a/johnzon-jsonschema/src/test/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorTest.java
 
b/johnzon-jsonschema/src/test/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorTest.java
new file mode 100644
index 0000000..a9f7e3c
--- /dev/null
+++ 
b/johnzon-jsonschema/src/test/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorTest.java
@@ -0,0 +1,351 @@
+/*
+ * 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.johnzon.jsonschema;
+
+import static java.util.Collections.emptyMap;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+
+import javax.json.Json;
+import javax.json.JsonBuilderFactory;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JsonSchemaValidatorTest {
+    private static JsonSchemaValidatorFactory factory;
+
+    private final JsonBuilderFactory jsonFactory = 
Json.createBuilderFactory(emptyMap());
+
+    @BeforeClass
+    public static void init() {
+        factory = new JsonSchemaValidatorFactory();
+    }
+
+    @AfterClass
+    public static void destroy() {
+        factory.close();
+    }
+
+    @Test
+    public void rootRequired() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                        .add("name", jsonFactory.createObjectBuilder()
+                                .add("type", "string")
+                            .build())
+                        .add("age", jsonFactory.createObjectBuilder()
+                                .add("type", "number")
+                            .build())
+                    .build())
+                .add("required", 
jsonFactory.createArrayBuilder().add("name").build())
+                .build());
+
+        final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder().add("name", "ok").build());
+        assertTrue(success.getErrors().toString(), success.isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().addNull("name").build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/", error.getField());
+        assertEquals("name is required and is not present", 
error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void rootType() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                        .add("name", jsonFactory.createObjectBuilder()
+                                .add("type", "string")
+                            .build())
+                        .add("age", jsonFactory.createObjectBuilder()
+                                .add("type", "number")
+                            .build())
+                    .build())
+                .build());
+
+        {
+            final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder().add("name", "ok").build());
+            assertTrue(success.getErrors().toString(), success.isSuccess());
+        }
+        {
+            final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder().addNull("name").build());
+            assertTrue(success.getErrors().toString(), success.isSuccess());
+        }
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().add("name", 5).build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/name", error.getField());
+        assertEquals("Expected STRING but got NUMBER", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void nestedType() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                        .add("person", jsonFactory.createObjectBuilder()
+                                .add("type", "object")
+                                .add("properties", 
jsonFactory.createObjectBuilder()
+                                    .add("name", 
jsonFactory.createObjectBuilder()
+                                            .add("type", "string")
+                                            .build())
+                                    .add("age", 
jsonFactory.createObjectBuilder()
+                                            .add("type", "number")
+                                            .build())
+                                    .build())
+                                .build())
+                    .build())
+                .build());
+
+        final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder()
+                .add("person", jsonFactory.createObjectBuilder()
+                        .add("name", "ok")
+                        .build())
+                .build());
+        assertTrue(success.getErrors().toString(), success.isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder()
+                .add("person", jsonFactory.createObjectBuilder()
+                        .add("name", jsonFactory.createObjectBuilder().build())
+                        .build())
+                .build());
+        assertFalse(failure.toString(), failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/person/name", error.getField());
+        assertEquals("Expected STRING but got OBJECT", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void enumValues() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("name", jsonFactory.createObjectBuilder()
+                            .add("type", "string")
+                            .add("enum", 
jsonFactory.createArrayBuilder().add("a").add("b").build())
+                            .build())
+                    .build())
+                .build());
+
+        final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder().add("name", "a").build());
+        assertTrue(success.getErrors().toString(), success.isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().add("name", 5).build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/name", error.getField());
+        assertEquals("Expected STRING but got NUMBER", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void multipleOf() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("age", jsonFactory.createObjectBuilder()
+                            .add("type", "number")
+                            .add("multipleOf", 5)
+                            .build())
+                    .build())
+                .build());
+
+        final ValidationResult success = 
validator.apply(jsonFactory.createObjectBuilder().add("age", 5).build());
+        assertTrue(success.getErrors().toString(), success.isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().add("age", 6).build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/age", error.getField());
+        assertEquals("6.0 is not a multiple of 5.0", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void minimum() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("age", jsonFactory.createObjectBuilder()
+                            .add("type", "number")
+                            .add("minimum", 5)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
5).build()).isSuccess());
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
6).build()).isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().add("age", 2).build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/age", error.getField());
+        assertEquals("2.0 is less than 5.0", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void maximum() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("age", jsonFactory.createObjectBuilder()
+                            .add("type", "number")
+                            .add("maximum", 5)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
5).build()).isSuccess());
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
4).build()).isSuccess());
+
+        final ValidationResult failure = 
validator.apply(jsonFactory.createObjectBuilder().add("age", 6).build());
+        assertFalse(failure.isSuccess());
+        final Collection<ValidationResult.ValidationError> errors = 
failure.getErrors();
+        assertEquals(1, errors.size());
+        final ValidationResult.ValidationError error = 
errors.iterator().next();
+        assertEquals("/age", error.getField());
+        assertEquals("6.0 is more than 5.0", error.getMessage());
+
+        validator.close();
+    }
+
+    @Test
+    public void exclusiveMinimum() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("age", jsonFactory.createObjectBuilder()
+                            .add("type", "number")
+                            .add("exclusiveMinimum", 5)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
6).build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("age", 
5).build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("age", 
4).build()).isSuccess());
+        validator.close();
+    }
+
+    @Test
+    public void exclusiveMaximum() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("age", jsonFactory.createObjectBuilder()
+                            .add("type", "number")
+                            .add("exclusiveMaximum", 5)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("age", 
4).build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("age", 
5).build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("age", 
6).build()).isSuccess());
+
+        validator.close();
+    }
+
+    @Test
+    public void minLength() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("name", jsonFactory.createObjectBuilder()
+                            .add("type", "string")
+                            .add("minLength", 2)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"ok").build()).isSuccess());
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"okk").build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"-").build()).isSuccess());
+
+        validator.close();
+    }
+
+    @Test
+    public void maxLength() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("name", jsonFactory.createObjectBuilder()
+                            .add("type", "string")
+                            .add("maxLength", 2)
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"ok").build()).isSuccess());
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"-").build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"fail").build()).isSuccess());
+
+        validator.close();
+    }
+
+    @Test
+    public void pattern() {
+        final JsonSchemaValidator validator = 
factory.newInstance(jsonFactory.createObjectBuilder()
+                .add("type", "object")
+                .add("properties", jsonFactory.createObjectBuilder()
+                    .add("name", jsonFactory.createObjectBuilder()
+                            .add("type", "string")
+                            .add("pattern", "[a-z]")
+                            .build())
+                    .build())
+                .build());
+
+        
assertTrue(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"ok").build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"-").build()).isSuccess());
+        
assertFalse(validator.apply(jsonFactory.createObjectBuilder().add("name", 
"0").build()).isSuccess());
+
+        validator.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/947c91b8/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 6f31957..bc46feb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,7 @@
     <module>johnzon-websocket</module>
     <module>johnzon-jsonb</module>
     <module>johnzon-json-extras</module>
+    <module>johnzon-jsonschema</module>
   </modules>
 
   <dependencies>

Reply via email to