Isolating the usage of json-schema validator to drools-pdp

- only drools-pdp have this validation, so moving the class and
its dependencies to there to avoid loading dependencies where it's
not used.

Issue-ID: POLICY-5084
Change-Id: I782e926aac7576c34401c9fef3cfcd3a98350a7f
Signed-off-by: adheli.tavares <adheli.tavares@est.tech>
diff --git a/policy-utils/pom.xml b/policy-utils/pom.xml
index 048c8c2..1483241 100644
--- a/policy-utils/pom.xml
+++ b/policy-utils/pom.xml
@@ -67,8 +67,8 @@
             <artifactId>guava</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.networknt</groupId>
-            <artifactId>json-schema-validator</artifactId>
+            <groupId>net.jimblackler.jsonschemafriend</groupId>
+            <artifactId>core</artifactId>
         </dependency>
     </dependencies>
 </project>
diff --git a/policy-utils/src/main/java/org/onap/policy/drools/exception/CoderRuntimeException.java b/policy-utils/src/main/java/org/onap/policy/drools/exception/CoderRuntimeException.java
new file mode 100644
index 0000000..f98c12b
--- /dev/null
+++ b/policy-utils/src/main/java/org/onap/policy/drools/exception/CoderRuntimeException.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Modifications Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.exception;
+
+/**
+ * Exceptions generated by coders.
+ */
+public class CoderRuntimeException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public CoderRuntimeException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/policy-utils/src/main/java/org/onap/policy/drools/policies/DomainMaker.java b/policy-utils/src/main/java/org/onap/policy/drools/policies/DomainMaker.java
index 506b097..84c81cf 100644
--- a/policy-utils/src/main/java/org/onap/policy/drools/policies/DomainMaker.java
+++ b/policy-utils/src/main/java/org/onap/policy/drools/policies/DomainMaker.java
@@ -28,7 +28,6 @@
 import org.apache.commons.lang3.StringUtils;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.coder.StandardCoder;
-import org.onap.policy.common.utils.coder.StandardValCoder;
 import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
diff --git a/policy-utils/src/main/java/org/onap/policy/drools/policies/StandardValCoder.java b/policy-utils/src/main/java/org/onap/policy/drools/policies/StandardValCoder.java
new file mode 100644
index 0000000..26608f3
--- /dev/null
+++ b/policy-utils/src/main/java/org/onap/policy/drools/policies/StandardValCoder.java
@@ -0,0 +1,142 @@
+/*--
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.policies;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import lombok.NonNull;
+import lombok.ToString;
+import net.jimblackler.jsonschemafriend.GenerationException;
+import net.jimblackler.jsonschemafriend.Schema;
+import net.jimblackler.jsonschemafriend.SchemaStore;
+import net.jimblackler.jsonschemafriend.ValidationException;
+import net.jimblackler.jsonschemafriend.Validator;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.drools.exception.CoderRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extension to the StandardCoder to support streaming validation against a Draft-07 Json schema specification.
+ * Extending specifically for drools policies - moved from common to here.
+ */
+
+@ToString
+public class StandardValCoder extends StandardCoder {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardValCoder.class);
+
+    private final Schema schema;
+
+    /**
+     * StandardCoder with validation.
+     */
+    public StandardValCoder(@NonNull String jsonSchema) {
+        try {
+            SchemaStore store = new SchemaStore();
+            this.schema = store.loadSchemaJson(jsonSchema);
+        } catch (GenerationException e) {
+            throw new CoderRuntimeException(e);
+        }
+    }
+
+    @Override
+    protected String toPrettyJson(Object object) {
+        try {
+            validate(gsonPretty.toJson(object));
+        } catch (CoderException e) {
+            throw new CoderRuntimeException(e);
+        }
+        return super.toPrettyJson(object);
+    }
+
+    @Override
+    protected String toJson(@NonNull Object object) {
+        var output = new StringWriter();
+        toJson(output, object);
+        return output.toString();
+    }
+
+    @Override
+    protected void toJson(@NonNull Writer target, @NonNull Object object) {
+        try {
+            validate(gson.toJson(object));
+        } catch (CoderException e) {
+            throw new CoderRuntimeException(e);
+        }
+        gson.toJson(object, object.getClass(), target);
+    }
+
+    @Override
+    protected <T> T fromJson(@NonNull Reader source, @NonNull Class<T> clazz) {
+        return convertFromDouble(clazz, gson.fromJson(source, clazz));
+    }
+
+    @Override
+    protected <T> T fromJson(String json, Class<T> clazz) {
+        try {
+            validate(json);
+        } catch (CoderException e) {
+            throw new CoderRuntimeException(e);
+        }
+        var reader = new StringReader(json);
+        return convertFromDouble(clazz, gson.fromJson(reader, clazz));
+    }
+
+    /**
+     * Is the json conformant?.
+     */
+    public boolean isConformant(@NonNull String json) {
+        try {
+            conformance(json);
+            return true;
+        } catch (Exception e) {
+            logger.error("JSON is not conformant to schema", e);
+            return false;
+        }
+    }
+
+    /**
+     * Check a json string for conformance against its schema definition.
+     */
+    public void conformance(@NonNull String json) throws CoderException {
+        validate(json);
+    }
+
+    private void validate(Object object) throws CoderException {
+        try {
+            final var validator = new Validator();
+            validator.validate(schema, object);
+        } catch (ValidationException exception) {
+            var error = String.format("JSON validation failed: %s", exception.getMessage());
+            logger.error(error);
+            throw new CoderException(error);
+        }
+    }
+
+    private void validate(String json) throws CoderException {
+        validate(gson.fromJson(json, Object.class));
+    }
+}
diff --git a/policy-utils/src/test/java/org/onap/policy/drools/policies/StandardValCoderTest.java b/policy-utils/src/test/java/org/onap/policy/drools/policies/StandardValCoderTest.java
new file mode 100644
index 0000000..43f537e
--- /dev/null
+++ b/policy-utils/src/test/java/org/onap/policy/drools/policies/StandardValCoderTest.java
@@ -0,0 +1,176 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ *  Modifications Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.policies;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.drools.exception.CoderRuntimeException;
+
+class StandardValCoderTest {
+    private String jsonSchema;
+    private String validJson;
+    private String missingReqJson;
+    private String badRegexJson;
+
+    @Data
+    @NoArgsConstructor
+    public static class ValOuter {
+        @Data
+        @NoArgsConstructor
+        public static class ValInner {
+            public String subItemString;
+            public Integer subItemInteger;
+        }
+
+        public String aaString;
+        public int anInteger;
+        public boolean aaBoolean;
+        public List<ValInner> aaCollection;
+    }
+
+    @BeforeEach
+    public void testSetUp() {
+        jsonSchema = getJson("src/test/resources/coder/test.schema.json");
+        validJson = getJson("src/test/resources/coder/valid.json");
+        missingReqJson = getJson("src/test/resources/coder/missing-required.json");
+        badRegexJson = getJson("src/test/resources/coder/bad-regex.json");
+    }
+
+    @Test
+    void testDecode() throws CoderException {
+        StandardValCoder valCoder = new StandardValCoder(jsonSchema);
+
+        ValOuter valOuter = valCoder.decode(validJson, ValOuter.class);
+        assertValidJson(valOuter);
+
+        StringReader reader = new StringReader(validJson);
+        valOuter = valCoder.decode(reader, ValOuter.class);
+        assertValidJson(valOuter);
+
+        try {
+            valCoder.decode(missingReqJson, ValOuter.class);
+            fail("missing required field should have been flagged by the schema validation");
+        } catch (CoderException e) {
+            assertThat(e.getMessage()).contains("Missing property aaCollection");
+        }
+
+        try {
+            valCoder.decode(badRegexJson, ValOuter.class);
+            fail("bad regex should have been flagged by the schema validation");
+        } catch (CoderException e) {
+            assertThat(e.getMessage())
+                .contains("Validation errors: \"abc123\" at #/aaString failed")
+                .contains("Did not match pattern: ^([a-z]*)$");
+        }
+    }
+
+    @Test
+    void testEncode() throws CoderException {
+        StandardValCoder valCoder = new StandardValCoder(jsonSchema);
+        ValOuter valOuter = valCoder.decode(validJson, ValOuter.class);
+
+        String valOuterJson = valCoder.encode(valOuter);
+        assertEquals(valOuter, valCoder.decode(valOuterJson, ValOuter.class));
+        assertValidJson(valOuter);
+
+        StringWriter writer = new StringWriter();
+        valCoder.encode(writer, valOuter);
+        assertEquals(valOuterJson, writer.toString());
+
+        // test exception case with an empty object
+        assertThatThrownBy(() -> valCoder.encode(new ValOuter())).isInstanceOf(CoderException.class);
+    }
+
+    @Test
+    void testPretty() throws CoderException {
+        StandardValCoder valCoder = new StandardValCoder(jsonSchema);
+        ValOuter valOuter = valCoder.decode(validJson, ValOuter.class);
+
+        String valOuterJson = valCoder.encode(valOuter);
+        assertEquals(valOuterJson, valCoder.encode(valOuter, false));
+        String prettyValOuterJson = valCoder.encode(valOuter, true);
+        assertNotEquals(valOuterJson, prettyValOuterJson);
+
+        assertEquals(valOuter, valCoder.decode(prettyValOuterJson, ValOuter.class));
+
+        // test exception cases with an empty object
+        assertThatThrownBy(() -> valCoder.encode(new ValOuter(), false)).isInstanceOf(CoderException.class);
+        assertThatThrownBy(() -> valCoder.encode(new ValOuter(), true)).isInstanceOf(CoderException.class);
+    }
+
+    @Test
+    void testConformance() {
+        StandardValCoder valCoder = new StandardValCoder(jsonSchema);
+        assertTrue(valCoder.isConformant(validJson));
+        assertFalse(valCoder.isConformant(missingReqJson));
+        assertFalse(valCoder.isConformant(badRegexJson));
+    }
+
+    @Test
+    void testNullValues() throws CoderException {
+        StandardValCoder valCoder = new StandardValCoder(jsonSchema);
+        assertThrows(NullPointerException.class, () -> valCoder.toJson(null));
+        assertThrows(NullPointerException.class, () -> valCoder.isConformant(null));
+        assertThrows(NullPointerException.class, () -> valCoder.conformance(null));
+
+        assertThrows(NullPointerException.class, () -> valCoder.toJson(null, null));
+        var writer = new StringWriter();
+        assertThrows(NullPointerException.class, () -> valCoder.toJson(writer, null));
+        ValOuter valOuter = valCoder.decode(validJson, ValOuter.class);
+        assertThrows(NullPointerException.class, () -> valCoder.toJson(null, valOuter));
+    }
+
+    @Test
+    void testConstructor() {
+        assertThrows(NullPointerException.class, () -> new StandardValCoder(null));
+
+        assertThrows(CoderRuntimeException.class, () -> new StandardValCoder("$schema"));
+    }
+
+    private void assertValidJson(ValOuter valOuter) {
+        assertEquals("abcd", valOuter.getAaString());
+        assertEquals(90, valOuter.getAnInteger());
+        assertTrue(valOuter.isAaBoolean());
+        assertEquals("defg", valOuter.getAaCollection().get(0).getSubItemString());
+        assertEquals(Integer.valueOf(1200), valOuter.getAaCollection().get(0).getSubItemInteger());
+    }
+
+    private String getJson(String filePath) {
+        return ResourceUtils.getResourceAsString(filePath);
+    }
+}
\ No newline at end of file
diff --git a/policy-utils/src/test/resources/coder/bad-regex.json b/policy-utils/src/test/resources/coder/bad-regex.json
new file mode 100644
index 0000000..049de2c
--- /dev/null
+++ b/policy-utils/src/test/resources/coder/bad-regex.json
@@ -0,0 +1,9 @@
+{
+    "aaString": "abc123",
+    "anInteger": 90,
+    "aaBoolean": true,
+    "aaCollection": [ {
+        "subItemString": "defg",
+        "subItemInteger": 1200
+    }]
+}
\ No newline at end of file
diff --git a/policy-utils/src/test/resources/coder/missing-required.json b/policy-utils/src/test/resources/coder/missing-required.json
new file mode 100644
index 0000000..e19db9d
--- /dev/null
+++ b/policy-utils/src/test/resources/coder/missing-required.json
@@ -0,0 +1,5 @@
+{
+    "aaString": "abcd",
+    "anInteger": 90,
+    "aaBoolean": true
+}
\ No newline at end of file
diff --git a/policy-utils/src/test/resources/coder/test.schema.json b/policy-utils/src/test/resources/coder/test.schema.json
new file mode 100644
index 0000000..60e70fb
--- /dev/null
+++ b/policy-utils/src/test/resources/coder/test.schema.json
@@ -0,0 +1,71 @@
+{
+  "definitions": {},
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "http://onap.org/policy/common/coders/root.json",
+  "type": "object",
+  "title": "Test Schema",
+  "required": [
+    "aaString",
+    "anInteger",
+    "aaBoolean",
+    "aaCollection"
+  ],
+  "properties": {
+    "aaString": {
+      "$id": "#/properties/aaString",
+      "type": "string",
+      "title": "an alphabetical string",
+      "default": "",
+      "examples": [
+        "abcdef"
+      ],
+      "pattern": "^([a-z]*)$"
+    },
+    "anInteger": {
+      "$id": "#/properties/anInteger",
+      "type": "integer",
+      "title": "a bounded integer",
+      "default": 5,
+      "examples": [
+        98
+      ],
+      "minimum": 10,
+      "maximum": 100
+    },
+    "aaBoolean": {
+      "$id": "#/properties/aaBoolean",
+      "type": "boolean",
+      "title": "a boolean",
+      "default": false,
+      "examples": [
+        true
+      ]
+    },
+    "aaCollection": {
+      "$id": "#/properties/aaCollection",
+      "type": "array",
+      "title": "a collection",
+      "items": {
+        "$id": "#/properties/aaCollection/items",
+        "type": "object",
+        "title": "the collection items",
+        "required": [
+          "subItemString"
+        ],
+        "properties": {
+          "subItemString": {
+            "$id": "#/properties/aaCollection/items/properties/subItemString",
+            "type": "string",
+            "title": "the subitem string",
+            "default": "blah",
+            "pattern": "^(.*)$"
+          },
+          "subItemInteger": {
+            "$id": "#/properties/aaCollection/items/properties/subItemInteger",
+            "type": "integer"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/policy-utils/src/test/resources/coder/valid.json b/policy-utils/src/test/resources/coder/valid.json
new file mode 100644
index 0000000..a2cdb93
--- /dev/null
+++ b/policy-utils/src/test/resources/coder/valid.json
@@ -0,0 +1,11 @@
+{
+  "aaString": "abcd",
+  "anInteger": 90,
+  "aaBoolean": true,
+  "aaCollection": [
+    {
+      "subItemString": "defg",
+      "subItemInteger": 1200
+    }
+  ]
+}
\ No newline at end of file