You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by jk...@apache.org on 2022/03/23 14:04:04 UTC
[unomi] branch master updated: UNOMI-555 : add possiblity to store jsonSchema (#393)
This is an automated email from the ASF dual-hosted git repository.
jkevan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new 15f5c3b UNOMI-555 : add possiblity to store jsonSchema (#393)
15f5c3b is described below
commit 15f5c3b7cbce14c5f933564f20189fb2366ef20e
Author: jsinovassin <58...@users.noreply.github.com>
AuthorDate: Wed Mar 23 15:04:00 2022 +0100
UNOMI-555 : add possiblity to store jsonSchema (#393)
* UNOMI-555 : add possiblity to store jsonSchema
* handle feedback
---
.../apache/unomi/api/schema/UnomiJSONSchema.java | 71 +++++
.../unomi/api/schema/json/JSONArrayType.java | 6 +-
.../unomi/api/schema/json/JSONBooleanType.java | 6 +-
.../apache/unomi/api/schema/json/JSONEnumType.java | 6 +-
.../unomi/api/schema/json/JSONIntegerType.java | 6 +-
.../apache/unomi/api/schema/json/JSONNullType.java | 6 +-
.../unomi/api/schema/json/JSONNumberType.java | 6 +-
.../unomi/api/schema/json/JSONObjectType.java | 18 +-
.../apache/unomi/api/schema/json/JSONSchema.java | 7 +-
.../unomi/api/schema/json/JSONStringType.java | 6 +-
.../org/apache/unomi/api/schema/json/JSONType.java | 19 +-
.../unomi/api/schema/json/JSONTypeFactory.java | 16 +-
.../apache/unomi/api/services/SchemaRegistry.java | 78 ++++-
extensions/groovy-actions/rest/pom.xml | 2 +-
.../graphql/schema/GraphQLSchemaProvider.java | 30 +-
.../test/java/org/apache/unomi/itests/AllITs.java | 3 +-
.../test/java/org/apache/unomi/itests/BaseIT.java | 17 +-
.../org/apache/unomi/itests/ContextServletIT.java | 65 +++--
.../java/org/apache/unomi/itests/JSONSchemaIT.java | 131 +++++++++
.../java/org/apache/unomi/itests/TestUtils.java | 9 +
.../resources/schemas/events/test-event-type.json | 3 +-
.../{test-event-type.json => test-invalid.json} | 6 +-
.../main/resources/etc/custom.system.properties | 2 +
.../META-INF/cxs/mappings/jsonschema.json | 25 ++
pom.xml | 1 +
.../unomi/rest/endpoints/JsonSchemaEndPoint.java | 109 +++++++
.../services/impl/events/EventServiceImpl.java | 3 +-
.../services/impl/schemas/SchemaRegistryImpl.java | 317 +++++++++++----------
.../impl/schemas/UnomiPropertyTypeKeyword.java | 37 ++-
.../services/listener/JsonSchemaListener.java | 170 +++++++++++
.../resources/OSGI-INF/blueprint/blueprint.xml | 20 +-
.../main/resources/org.apache.unomi.services.cfg | 5 +-
.../shell/commands/DeploymentCommandSupport.java | 4 +-
33 files changed, 927 insertions(+), 283 deletions(-)
diff --git a/api/src/main/java/org/apache/unomi/api/schema/UnomiJSONSchema.java b/api/src/main/java/org/apache/unomi/api/schema/UnomiJSONSchema.java
new file mode 100644
index 0000000..3ae5077
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/schema/UnomiJSONSchema.java
@@ -0,0 +1,71 @@
+/*
+ * 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.unomi.api.schema;
+
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.MetadataItem;
+
+/**
+ * Object which represents a JSON schema stored in the persistence service
+ */
+public class UnomiJSONSchema extends MetadataItem {
+ public static final String ITEM_TYPE = "jsonSchema";
+
+ private String id;
+ private String schema;
+ private String target;
+
+ public UnomiJSONSchema(){}
+
+ /**
+ * Instantiates a new JSON schema with an id and a schema as string
+ *
+ * @param id id of the schema
+ * @param schema as string
+ * @param target of the schema
+ */
+ public UnomiJSONSchema(String id, String schema, String target) {
+ super(new Metadata(id));
+ this.id = id;
+ this.schema = schema;
+ this.target = target;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public void setSchema(String schema) {
+ this.schema = schema;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONArrayType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONArrayType.java
index add106d..aa8cc2a 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONArrayType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONArrayType.java
@@ -16,8 +16,6 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.List;
import java.util.Map;
@@ -26,8 +24,8 @@ public class JSONArrayType extends JSONType {
List<JSONType> items;
JSONType contains;
- public JSONArrayType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONArrayType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("array");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONBooleanType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONBooleanType.java
index cfaa734..aea431e 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONBooleanType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONBooleanType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONBooleanType extends JSONType {
- public JSONBooleanType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONBooleanType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("boolean");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONEnumType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONEnumType.java
index d2e18a0..57ba1e6 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONEnumType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONEnumType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONEnumType extends JSONType {
- public JSONEnumType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONEnumType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("enum");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONIntegerType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONIntegerType.java
index 6aa9a32..9ae40dd 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONIntegerType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONIntegerType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONIntegerType extends JSONType {
- public JSONIntegerType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONIntegerType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("integer");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONNullType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONNullType.java
index ec0adde..809a29f 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONNullType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONNullType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONNullType extends JSONType {
- public JSONNullType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONNullType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("null");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONNumberType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONNumberType.java
index bb6bf13..d591777 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONNumberType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONNumberType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONNumberType extends JSONType {
- public JSONNumberType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONNumberType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("number");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONObjectType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONObjectType.java
index 45b25e0..da28f41 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONObjectType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONObjectType.java
@@ -16,37 +16,35 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JSONObjectType extends JSONType {
- Map<String,List<JSONType>> properties = new HashMap<>();
+ Map<String, List<JSONType>> properties = new HashMap<>();
JSONType additionalProperties;
- Map<String,List<JSONType>> patternProperties = new HashMap<>();
+ Map<String, List<JSONType>> patternProperties = new HashMap<>();
JSONType propertyNames;
int maxProperties;
- public JSONObjectType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONObjectType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("object");
- Map<String,Object> propertiesTree = (Map<String,Object>) schemaTree.get("properties");
+ Map<String, Object> propertiesTree = (Map<String, Object>) schemaTree.get("properties");
if (propertiesTree != null) {
propertiesTree.entrySet().forEach(entry -> {
- properties.put(entry.getKey(), jsonTypeFactory.getTypes((Map<String,Object>)entry.getValue()));
+ properties.put(entry.getKey(), jsonTypeFactory.getTypes((Map<String, Object>) entry.getValue()));
});
}
}
- public Map<String,List<JSONType>> getProperties() {
+ public Map<String, List<JSONType>> getProperties() {
return properties;
}
- public void setProperties(Map<String,List<JSONType>> properties) {
+ public void setProperties(Map<String, List<JSONType>> properties) {
this.properties = properties;
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java
index 544d009..1e65f8e 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java
@@ -18,7 +18,6 @@
package org.apache.unomi.api.schema.json;
import org.apache.unomi.api.PluginType;
-import org.apache.unomi.api.services.SchemaRegistry;
import java.util.List;
import java.util.Map;
@@ -34,11 +33,11 @@ public class JSONSchema extends JSONType implements PluginType {
private String name;
private String version;
- public JSONSchema(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONSchema(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
schemaId = (String) schemaTree.get("$id");
if (schemaTree.containsKey("self")) {
- Map<String,Object> self = (Map<String,Object>) schemaTree.get("self");
+ Map<String, Object> self = (Map<String, Object>) schemaTree.get("self");
name = (String) self.get("name");
vendor = (String) self.get("vendor");
version = (String) self.get("version");
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONStringType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONStringType.java
index c1b9c10..c63f6b5 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONStringType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONStringType.java
@@ -16,13 +16,11 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.Map;
public class JSONStringType extends JSONType {
- public JSONStringType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
- super(schemaTree, jsonTypeFactory, schemaRegistry);
+ public JSONStringType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
+ super(schemaTree, jsonTypeFactory);
setType("string");
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONType.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONType.java
index 3b60b0f..d1c0474 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONType.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONType.java
@@ -16,8 +16,6 @@
*/
package org.apache.unomi.api.schema.json;
-import org.apache.unomi.api.services.SchemaRegistry;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -31,18 +29,15 @@ public class JSONType {
List<JSONType> anyOf;
List<JSONType> oneOf;
- Map<String,Object> customKeywords;
+ Map<String, Object> customKeywords;
- protected Map<String,Object> schemaTree;
+ protected Map<String, Object> schemaTree;
protected JSONTypeFactory jsonTypeFactory;
- protected SchemaRegistry schemaRegistry;
-
- public JSONType(Map<String,Object> schemaTree, JSONTypeFactory jsonTypeFactory, SchemaRegistry schemaRegistry) {
+ public JSONType(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) {
this.schemaTree = schemaTree;
this.jsonTypeFactory = jsonTypeFactory;
- this.schemaRegistry = schemaRegistry;
}
public String getName() {
@@ -69,20 +64,16 @@ public class JSONType {
return jsonTypeFactory;
}
- public SchemaRegistry getSchemaRegistry() {
- return schemaRegistry;
- }
-
public String getRef() {
ref = (String) schemaTree.get("$ref");
return ref;
}
public List<JSONType> getAllOf() {
- List<Map<String,Object>> allOfTree = (List<Map<String,Object>>) schemaTree.get("allOf");
+ List<Map<String, Object>> allOfTree = (List<Map<String, Object>>) schemaTree.get("allOf");
List<JSONType> allOfTypes = new ArrayList<>();
if (allOfTree != null) {
- for (Map<String,Object> allOfEntry : allOfTree) {
+ for (Map<String, Object> allOfEntry : allOfTree) {
List<JSONType> entryTypes = jsonTypeFactory.getTypes(allOfEntry);
allOfTypes.addAll(entryTypes);
}
diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONTypeFactory.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONTypeFactory.java
index da79016..05ac24d 100644
--- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONTypeFactory.java
+++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONTypeFactory.java
@@ -17,6 +17,8 @@
package org.apache.unomi.api.schema.json;
import org.apache.unomi.api.services.SchemaRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -27,6 +29,8 @@ import java.util.Map;
public class JSONTypeFactory {
+ private static final Logger logger = LoggerFactory.getLogger(JSONTypeFactory.class);
+
Map<String, Class<? extends JSONType>> jsonTypes = new HashMap<>();
SchemaRegistry schemaRegistry;
@@ -42,7 +46,7 @@ public class JSONTypeFactory {
jsonTypes.put("null", JSONNullType.class);
}
- List<JSONType> getTypes(Map<String,Object> schemaTree) {
+ List<JSONType> getTypes(Map<String, Object> schemaTree) {
if (schemaTree.containsKey("$ref")) {
String schemaId = (String) schemaTree.get("$ref");
JSONSchema refSchema = schemaRegistry.getSchema(schemaId);
@@ -54,7 +58,7 @@ public class JSONTypeFactory {
}
if (schemaTree.containsKey("enum")) {
List<JSONType> result = new ArrayList<>();
- result.add(new JSONEnumType(schemaTree, this, schemaRegistry));
+ result.add(new JSONEnumType(schemaTree, this));
return result;
}
Object typeObject = schemaTree.get("type");
@@ -77,12 +81,12 @@ public class JSONTypeFactory {
continue;
}
Class<? extends JSONType> typeClass = jsonTypes.get(type);
- Constructor<? extends JSONType> constructor = null;
+ Constructor<? extends JSONType> constructor;
try {
- constructor = typeClass.getConstructor(Map.class, JSONTypeFactory.class, SchemaRegistry.class);
- resultJsonTypes.add(constructor.newInstance(schemaTree, this, schemaRegistry));
+ constructor = typeClass.getConstructor(Map.class, JSONTypeFactory.class);
+ resultJsonTypes.add(constructor.newInstance(schemaTree, this));
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
- e.printStackTrace();
+ logger.error("Error while building object type", e);
}
}
return resultJsonTypes;
diff --git a/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java b/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java
index 1bdea07..b25a613 100644
--- a/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java
+++ b/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java
@@ -17,21 +17,93 @@
package org.apache.unomi.api.services;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.PartialList;
import org.apache.unomi.api.schema.json.JSONSchema;
+import java.io.IOException;
import java.io.InputStream;
import java.util.List;
+/**
+ * Service that allow to manage JSON schema. It allows to get, save and delete schemas
+ */
public interface SchemaRegistry {
+ /**
+ * Retrieves json schema metadatas, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them
+ * are retrieved, starting with the {@code
+ * offset}-th one.
+ *
+ * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements
+ * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved
+ * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the
+ * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by
+ * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}.
+ * @return a {@link PartialList} of json schema metadata
+ */
+ PartialList<Metadata> getJsonSchemaMetadatas(int offset, int size, String sortBy);
+
+ /**
+ * Verify if an object is valid against a schema
+ *
+ * @param object to validate
+ * @param schemaId id of the schema used for the validation
+ * @return true is the object is valid
+ */
boolean isValid(Object object, String schemaId);
+
+ /**
+ * Get a schema matching by a schema id
+ *
+ * @param schemaId Id of the schema
+ * @return A JSON schema
+ */
JSONSchema getSchema(String schemaId);
- List<JSONSchema> getTargetSchemas(String target);
+ /**
+ * Get a list a {@link org.apache.unomi.api.schema.json.JSONSchema}
+ *
+ * @param target to filter the schemas
+ * @return a list of JSONSchema
+ */
+ List<JSONSchema> getSchemasByTarget(String target);
+
+ /**
+ * Save a new schema or update a schema
+ *
+ * @param schema as a String value
+ */
+ void saveSchema(String schema);
+
+ /**
+ * Save a new schema or update a schema
+ *
+ * @param schemaStream inputStream of the schema
+ */
+ void saveSchema(InputStream schemaStream) throws IOException;
- String registerSchema(String target, InputStream jsonSchemaInputStream);
+ /**
+ * Load a predefined schema into memory
+ *
+ * @param schemaStream inputStream of the schema
+ */
+ void loadPredefinedSchema(InputStream schemaStream);
- boolean unregisterSchema(String target, String schemaId);
+ /**
+ * Delete a schema according to its id
+ *
+ * @param schemaId id of the schema to delete
+ * @return true if the schema has been deleted
+ */
+ boolean deleteSchema(String schemaId);
+ /**
+ * Delete a schema
+ *
+ * @param schemaStream inputStream of the schema to delete
+ * @return true if the schema has been deleted
+ */
+ boolean deleteSchema(InputStream schemaStream);
}
diff --git a/extensions/groovy-actions/rest/pom.xml b/extensions/groovy-actions/rest/pom.xml
index 30c0854..e1f7ff2 100644
--- a/extensions/groovy-actions/rest/pom.xml
+++ b/extensions/groovy-actions/rest/pom.xml
@@ -92,4 +92,4 @@
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/schema/GraphQLSchemaProvider.java b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/schema/GraphQLSchemaProvider.java
index 5a02c80..8347fd7 100644
--- a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/schema/GraphQLSchemaProvider.java
+++ b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/schema/GraphQLSchemaProvider.java
@@ -331,7 +331,7 @@ public class GraphQLSchemaProvider {
}
private void registerDynamicUnomiInputEvents(GraphQLSchema.Builder schemaBuilder) {
- final List<JSONSchema> unomiEventTypes = schemaRegistry.getTargetSchemas("events");
+ final List<JSONSchema> unomiEventTypes = schemaRegistry.getSchemasByTarget("events");
if (!unomiEventTypes.isEmpty()) {
for (JSONSchema unomiEventType : unomiEventTypes) {
@@ -353,7 +353,7 @@ public class GraphQLSchemaProvider {
}
private void registerDynamicUnomiOutputEvents(GraphQLSchema.Builder schemaBuilder) {
- final List<JSONSchema> unomiEventTypes = schemaRegistry.getTargetSchemas("events");
+ final List<JSONSchema> unomiEventTypes = schemaRegistry.getSchemasByTarget("events");
if (!unomiEventTypes.isEmpty()) {
final GraphQLCodeRegistry.Builder codeRegisterBuilder = graphQLAnnotations.getContainer().getCodeRegistryBuilder();
@@ -377,8 +377,8 @@ public class GraphQLSchemaProvider {
}
private void registerDynamicInputFilterFields(final String typeName,
- final Class<?> annotatedClass,
- final Collection<DefinitionType> propertyTypes) {
+ final Class<?> annotatedClass,
+ final Collection<DefinitionType> propertyTypes) {
final GraphQLInputObjectType originalObject = getInputObjectType(annotatedClass);
final List<GraphQLInputObjectField> inputObjectFields =
@@ -391,17 +391,17 @@ public class GraphQLSchemaProvider {
}
private void registerDynamicOutputFields(final String graphQLTypeName,
- final Class<?> annotatedClass,
- final Class<? extends DynamicFieldDataFetcher> fetcherClass,
- final Collection<DefinitionType> propertyTypes) {
+ final Class<?> annotatedClass,
+ final Class<? extends DynamicFieldDataFetcher> fetcherClass,
+ final Collection<DefinitionType> propertyTypes) {
final GraphQLObjectType objectType = graphQLAnnotations.object(annotatedClass);
registerDynamicOutputFields(graphQLTypeName, objectType, fetcherClass, propertyTypes);
}
private void registerDynamicOutputFields(final String graphQLTypeName,
- final GraphQLObjectType graphQLObjectType,
- final Class<? extends DynamicFieldDataFetcher> fetcherClass,
- final Collection<DefinitionType> propertyTypes) {
+ final GraphQLObjectType graphQLObjectType,
+ final Class<? extends DynamicFieldDataFetcher> fetcherClass,
+ final Collection<DefinitionType> propertyTypes) {
final GraphQLCodeRegistry.Builder codeRegisterBuilder = graphQLAnnotations.getContainer().getCodeRegistryBuilder();
final List<GraphQLFieldDefinition> fieldDefinitions = new ArrayList<>();
@@ -572,15 +572,15 @@ public class GraphQLSchemaProvider {
}
private void registerDynamicInputFields(final String graphQLTypeName,
- final Class<?> clazz,
- final Collection<DefinitionType> propertyTypes) {
+ final Class<?> clazz,
+ final Collection<DefinitionType> propertyTypes) {
final GraphQLInputObjectType inputObjectType = getInputObjectType(clazz);
registerDynamicInputFields(graphQLTypeName, inputObjectType, propertyTypes);
}
private void registerDynamicInputFields(final String graphQLTypeName,
- final GraphQLInputObjectType graphQLInputObjectType,
- final Collection<DefinitionType> propertyTypes) {
+ final GraphQLInputObjectType graphQLInputObjectType,
+ final Collection<DefinitionType> propertyTypes) {
final List<GraphQLInputObjectField> fieldDefinitions = new ArrayList<>();
propertyTypes.forEach(propertyType -> {
@@ -650,7 +650,7 @@ public class GraphQLSchemaProvider {
}
// now add all unomi defined event types
- final List<JSONSchema> unomiEventTypes = schemaRegistry.getTargetSchemas("events");
+ final List<JSONSchema> unomiEventTypes = schemaRegistry.getSchemasByTarget("events");
unomiEventTypes.forEach(eventType -> {
final String typeName = UnomiToGraphQLConverter.convertEventType(eventType.getName());
final GraphQLInputType eventInputType = (GraphQLInputType) getFromTypeRegistry(typeName + "Input");
diff --git a/itests/src/test/java/org/apache/unomi/itests/AllITs.java b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
index facb730..b198a93 100644
--- a/itests/src/test/java/org/apache/unomi/itests/AllITs.java
+++ b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
@@ -58,7 +58,8 @@ import org.junit.runners.Suite.SuiteClasses;
GraphQLProfileIT.class,
GraphQLProfilePropertiesIT.class,
GraphQLSegmentIT.class,
- GraphQLWebSocketIT.class
+ GraphQLWebSocketIT.class,
+ JSONSchemaIT.class
})
public class AllITs {
}
diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index 102d107..767020a 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -40,7 +40,6 @@ import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.api.services.DefinitionsService;
import org.apache.unomi.api.services.RulesService;
-import org.apache.unomi.api.services.SchemaRegistry;
import org.apache.unomi.lifecycle.BundleWatcher;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
@@ -130,10 +129,6 @@ public abstract class BaseIT {
@Filter(timeout = 600000)
protected ConfigurationAdmin configurationAdmin;
- @Inject
- @Filter(timeout = 600000)
- protected SchemaRegistry schemaRegistry;
-
private CloseableHttpClient httpClient;
@Before
@@ -331,7 +326,6 @@ public abstract class BaseIT {
persistenceService = getService(PersistenceService.class);
definitionsService = getService(DefinitionsService.class);
rulesService = getService(RulesService.class);
- schemaRegistry = getService(SchemaRegistry.class);
}
public void updateConfiguration(String serviceName, String configPid, String propName, Object propValue) throws InterruptedException, IOException {
@@ -432,13 +426,13 @@ public abstract class BaseIT {
return null;
}
- protected CloseableHttpResponse post(final String url, final String resource) {
+ protected CloseableHttpResponse post(final String url, final String resource, ContentType contentType) {
try {
final HttpPost request = new HttpPost(getFullUrl(url));
if (resource != null) {
final String resourceAsString = resourceAsString(resource);
- request.setEntity(new StringEntity(resourceAsString, JSON_CONTENT_TYPE));
+ request.setEntity(new StringEntity(resourceAsString, contentType));
}
return executeHttpRequest(request);
@@ -448,7 +442,11 @@ public abstract class BaseIT {
return null;
}
- protected void delete(final String url) {
+ protected CloseableHttpResponse post(final String url, final String resource) {
+ return post(url,resource, JSON_CONTENT_TYPE);
+ }
+
+ protected CloseableHttpResponse delete(final String url) {
CloseableHttpResponse response = null;
try {
final HttpDelete httpDelete = new HttpDelete(getFullUrl(url));
@@ -466,6 +464,7 @@ public abstract class BaseIT {
}
}
}
+ return response;
}
protected CloseableHttpResponse executeHttpRequest(HttpUriRequest request) throws IOException {
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index 14a6c98..60548a7 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -37,11 +37,12 @@ import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerSuite;
import org.ops4j.pax.exam.util.Filter;
import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -60,6 +61,8 @@ import static org.junit.Assert.*;
@ExamReactorStrategy(PerSuite.class)
public class ContextServletIT extends BaseIT {
private final static String CONTEXT_URL = "/cxs/context.json";
+ private final static String JSONSCHEMA_URL = "/cxs/jsonSchema";
+
private final static String THIRD_PARTY_HEADER_NAME = "X-Unomi-Peer";
private final static String TEST_EVENT_TYPE = "test-event-type";
private final static String TEST_EVENT_TYPE_SCHEMA = "test-event-type.json";
@@ -68,8 +71,13 @@ public class ContextServletIT extends BaseIT {
private final static String SEGMENT_ID = "test-segment-id";
private final static int SEGMENT_NUMBER_OF_DAYS = 30;
+ private static final int DEFAULT_TRYING_TIMEOUT = 2000;
+ private static final int DEFAULT_TRYING_TRIES = 30;
+
private ObjectMapper objectMapper = new ObjectMapper();
+ private final static Logger LOGGER = LoggerFactory.getLogger(ContextServletIT.class);
+
@Inject
@Filter(timeout = 600000)
protected EventService eventService;
@@ -117,6 +125,9 @@ public class ContextServletIT extends BaseIT {
profile = new Profile(profileId);
profileService.save(profile);
+ keepTrying("Couldn't find json schema endpoint",
+ () -> get(JSONSCHEMA_URL, List.class), Objects::nonNull,
+ DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
refreshPersistence();
}
@@ -128,13 +139,21 @@ public class ContextServletIT extends BaseIT {
profileService.delete(profile.getItemId(), false);
segmentService.removeSegmentDefinition(SEGMENT_ID, false);
- schemaRegistry.unregisterSchema("events", TEST_EVENT_TYPE);
- schemaRegistry.unregisterSchema("events", FLOAT_PROPERTY_EVENT_TYPE);
+ String encodedString = Base64.getEncoder()
+ .encodeToString("https://unomi.apache.org/schemas/json/events/test-event-type/1-0-0".getBytes());
+ delete(JSONSCHEMA_URL + "/" + encodedString);
+
+ encodedString = Base64.getEncoder()
+ .encodeToString("https://unomi.apache.org/schemas/json/events/float-property-type/1-0-0".getBytes());
+ delete(JSONSCHEMA_URL + "/" + encodedString);
+
+ encodedString = Base64.getEncoder()
+ .encodeToString("https://unomi.apache.org/schemas/json/events/float-property-type/1-0-0".getBytes());
+ delete(JSONSCHEMA_URL + "/" + encodedString);
}
- private void registerEventType(String jsonSchemaFileName) throws IOException {
- InputStream jsonSchemaInputStream = bundleContext.getBundle().getResource("schemas/events/" + jsonSchemaFileName).openStream();
- schemaRegistry.registerSchema("events", jsonSchemaInputStream);
+ private void registerEventType(String jsonSchemaFileName) {
+ post(JSONSCHEMA_URL, "schemas/events/" + jsonSchemaFileName, ContentType.TEXT_PLAIN);
}
@Test
@@ -161,7 +180,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(URL + CONTEXT_URL);
request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request, sessionId);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -195,7 +214,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setSessionId(session.getItemId());
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request, sessionId);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -227,7 +246,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setSessionId(session.getItemId());
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request, sessionId);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -253,7 +272,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
refreshPersistence();
Thread.sleep(1000); //Making sure DB is updated
@@ -287,7 +306,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(regularURI);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
//The first event is with a default timestamp (now)
String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
refreshPersistence();
@@ -319,7 +338,7 @@ public class ContextServletIT extends BaseIT {
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(regularURI);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
//The first event is with a default timestamp (now)
TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, sessionId);
String cookieHeaderValue = response.getCookieHeaderValue();
@@ -351,7 +370,7 @@ public class ContextServletIT extends BaseIT {
//Act
HttpPost request = new HttpPost(URL + CONTEXT_URL);
request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -381,7 +400,7 @@ public class ContextServletIT extends BaseIT {
//Act
HttpPost request = new HttpPost(URL + CONTEXT_URL);
request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -412,7 +431,7 @@ public class ContextServletIT extends BaseIT {
//Act
HttpPost request = new HttpPost(URL + CONTEXT_URL);
request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -442,7 +461,7 @@ public class ContextServletIT extends BaseIT {
//Act
HttpPost request = new HttpPost(URL + CONTEXT_URL);
request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -465,7 +484,7 @@ public class ContextServletIT extends BaseIT {
Map<String, String> parameters = new HashMap<>();
parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json", parameters), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -487,7 +506,7 @@ public class ContextServletIT extends BaseIT {
Map<String, String> parameters = new HashMap<>();
parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", parameters), ContentType.APPLICATION_JSON));
TestUtils.executeContextJSONRequest(request);
refreshPersistence();
Thread.sleep(2000); //Making sure event is updated in DB
@@ -501,7 +520,7 @@ public class ContextServletIT extends BaseIT {
Map<String,String> parameters = new HashMap<>();
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("personalization.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization.json", parameters), ContentType.APPLICATION_JSON));
TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
refreshPersistence();
@@ -515,7 +534,7 @@ public class ContextServletIT extends BaseIT {
Map<String,String> parameters = new HashMap<>();
parameters.put("storeInSession", "false");
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.APPLICATION_JSON));
TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
refreshPersistence();
@@ -539,7 +558,7 @@ public class ContextServletIT extends BaseIT {
// now let's test with session storage
parameters.put("storeInSession", "true");
request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.APPLICATION_JSON));
response = TestUtils.executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
refreshPersistence();
@@ -603,7 +622,7 @@ public class ContextServletIT extends BaseIT {
// first let's make sure everything works without the requireScoring parameter
parameters = new HashMap<>();
HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("withoutRequireScores.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("withoutRequireScores.json", parameters), ContentType.APPLICATION_JSON));
TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
refreshPersistence();
@@ -616,7 +635,7 @@ public class ContextServletIT extends BaseIT {
// now let's test adding it.
parameters = new HashMap<>();
request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new StringEntity(getValidatedBundleJSON("withRequireScores.json", parameters), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("withRequireScores.json", parameters), ContentType.APPLICATION_JSON));
response = TestUtils.executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
refreshPersistence();
diff --git a/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
new file mode 100644
index 0000000..ce4e4c8
--- /dev/null
+++ b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
@@ -0,0 +1,131 @@
+/*
+ * 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.unomi.itests;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.unomi.api.schema.UnomiJSONSchema;
+import org.apache.unomi.api.services.SchemaRegistry;
+import org.apache.unomi.persistence.spi.PersistenceService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerSuite;
+import org.ops4j.pax.exam.util.Filter;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Class to tests the JSON schema features
+ */
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerSuite.class)
+public class JSONSchemaIT extends BaseIT {
+ private final static String JSONSCHEMA_URL = "/cxs/jsonSchema";
+ private static final int DEFAULT_TRYING_TIMEOUT = 2000;
+ private static final int DEFAULT_TRYING_TRIES = 30;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected SchemaRegistry schemaRegistry;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected PersistenceService persistenceService;
+
+ @Before
+ public void setUp() throws InterruptedException {
+ keepTrying("Couldn't find json schema endpoint", () -> get(JSONSCHEMA_URL, List.class), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
+ DEFAULT_TRYING_TRIES);
+ }
+
+ @After
+ public void tearDown() {
+ schemaRegistry.deleteSchema("https://unomi.apache.org/schemas/json/events/test-event-type/1-0-0");
+ }
+
+ @Test
+ public void testGetJsonSchemasMetadatas() throws InterruptedException {
+ List jsonSchemas = get(JSONSCHEMA_URL, List.class);
+ assertTrue("JSON schema list should be empty", jsonSchemas.isEmpty());
+
+ post(JSONSCHEMA_URL, "schemas/events/test-event-type.json", ContentType.TEXT_PLAIN);
+
+ refreshPersistence();
+ jsonSchemas = keepTrying("Couldn't find json schemas", () -> get(JSONSCHEMA_URL, List.class), (list) -> !list.isEmpty(),
+ DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+ assertFalse("JSON schema list should not be empty", jsonSchemas.isEmpty());
+ assertEquals("JSON schema list should not be empty", 1, jsonSchemas.size());
+ }
+
+ @Test
+ public void testSaveNewValidJSONSchema() throws InterruptedException {
+
+ assertTrue("JSON schema list should be empty", persistenceService.getAllItems(UnomiJSONSchema.class).isEmpty());
+
+ CloseableHttpResponse response = post(JSONSCHEMA_URL, "schemas/events/test-event-type.json", ContentType.TEXT_PLAIN);
+
+ assertEquals("Invalid response code", 200, response.getStatusLine().getStatusCode());
+ refreshPersistence();
+ List jsonSchemas = keepTrying("Couldn't find json schemas", () -> get(JSONSCHEMA_URL, List.class), (list) -> !list.isEmpty(),
+ DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+ assertFalse("JSON schema list should not be empty", jsonSchemas.isEmpty());
+ }
+
+ @Test
+ public void testDeleteJSONSchema() throws InterruptedException {
+ assertTrue("JSON schema list should be empty", persistenceService.getAllItems(UnomiJSONSchema.class).isEmpty());
+
+ post(JSONSCHEMA_URL, "schemas/events/test-event-type.json", ContentType.TEXT_PLAIN);
+
+ refreshPersistence();
+ keepTrying("Couldn't find json schemas", () -> get(JSONSCHEMA_URL, List.class), (list) -> !list.isEmpty(), DEFAULT_TRYING_TIMEOUT,
+ DEFAULT_TRYING_TRIES);
+
+ String encodedString = Base64.getEncoder()
+ .encodeToString("https://unomi.apache.org/schemas/json/events/test-event-type/1-0-0".getBytes());
+ CloseableHttpResponse response = delete(JSONSCHEMA_URL + "/" + encodedString);
+ assertEquals("Invalid response code", 204, response.getStatusLine().getStatusCode());
+
+ refreshPersistence();
+ List jsonSchemas = keepTrying("wait for empty list of schemas", () -> get(JSONSCHEMA_URL, List.class), List::isEmpty,
+ DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+
+ assertTrue("JSON schema list should be empty", jsonSchemas.isEmpty());
+ }
+
+ @Test
+ public void testSaveNewInvalidJSONSchema() throws IOException {
+ assertTrue("JSON schema list should be empty", persistenceService.getAllItems(UnomiJSONSchema.class).isEmpty());
+ try (CloseableHttpResponse response = post(JSONSCHEMA_URL, "schemas/events/test-invalid.json", ContentType.TEXT_PLAIN)) {
+ assertEquals("Save should have failed", 500, response.getStatusLine().getStatusCode());
+ }
+ }
+}
diff --git a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
index 7416d46..0b4073e 100644
--- a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
+++ b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
@@ -18,13 +18,22 @@
package org.apache.unomi.itests;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.unomi.api.ContextResponse;
import org.apache.unomi.api.Event;
diff --git a/itests/src/test/resources/schemas/events/test-event-type.json b/itests/src/test/resources/schemas/events/test-event-type.json
index 46b56e2..99ba3f8 100644
--- a/itests/src/test/resources/schemas/events/test-event-type.json
+++ b/itests/src/test/resources/schemas/events/test-event-type.json
@@ -5,9 +5,10 @@
"vendor":"org.apache.unomi",
"name":"events/test-event-type",
"format":"jsonschema",
+ "target":"events",
"version":"1-0-0"
},
"title": "TestEvent",
"type": "object",
"allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/event/1-0-0" }]
-}
\ No newline at end of file
+}
diff --git a/itests/src/test/resources/schemas/events/test-event-type.json b/itests/src/test/resources/schemas/events/test-invalid.json
similarity index 70%
copy from itests/src/test/resources/schemas/events/test-event-type.json
copy to itests/src/test/resources/schemas/events/test-invalid.json
index 46b56e2..794a77c 100644
--- a/itests/src/test/resources/schemas/events/test-event-type.json
+++ b/itests/src/test/resources/schemas/events/test-invalid.json
@@ -1,13 +1,13 @@
{
- "$id": "https://unomi.apache.org/schemas/json/events/test-event-type/1-0-0",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"self":{
"vendor":"org.apache.unomi",
- "name":"events/test-event-type",
+ "name":"events/invalid",
"format":"jsonschema",
"version":"1-0-0"
},
+ "invalidEntry": "An invalid entry",
"title": "TestEvent",
"type": "object",
"allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/event/1-0-0" }]
-}
\ No newline at end of file
+}
diff --git a/package/src/main/resources/etc/custom.system.properties b/package/src/main/resources/etc/custom.system.properties
index fbb3a4d..3d9d2cf 100644
--- a/package/src/main/resources/etc/custom.system.properties
+++ b/package/src/main/resources/etc/custom.system.properties
@@ -183,6 +183,8 @@ org.apache.unomi.rules.statistics.refresh.interval=${env:UNOMI_RULES_STATISTICS_
org.apache.unomi.rules.optimizationActivated=${env:UNOMI_RULES_OPTIMIZATION_ACTIVATED:-true}
# The number of threads to compose the pool size of the scheduler.
org.apache.unomi.scheduler.thread.poolSize=${env:UNOMI_SCHEDULER_THREAD_POOL_SIZE:-5}
+# When performing json schema updates,
+services.json.schema.refresh.interval=${env:UNOMI_JSON_SCHEMA_REFRESH_INTERVAL:-1000}
#######################################################################################################################
## Third Party server settings ##
diff --git a/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/jsonschema.json b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/jsonschema.json
new file mode 100644
index 0000000..896e1be
--- /dev/null
+++ b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/jsonschema.json
@@ -0,0 +1,25 @@
+{
+ "dynamic_templates": [
+ {
+ "all": {
+ "match": "*",
+ "match_mapping_type": "string",
+ "mapping": {
+ "type": "text",
+ "analyzer": "folding",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "schema": {
+ "type": "text"
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 4f3e41e..0874955 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,7 @@
<version.pax.exam>4.13.1</version.pax.exam>
<elasticsearch.version>7.4.2</elasticsearch.version>
<groovy.version>3.0.3</groovy.version>
+ <networknt.version>1.0.49</networknt.version>
<bean.validation.version>1.1.0.Final</bean.validation.version>
<hibernate.validator.version>5.4.3.Final</hibernate.validator.version>
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/JsonSchemaEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/JsonSchemaEndPoint.java
new file mode 100644
index 0000000..96992d2
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/JsonSchemaEndPoint.java
@@ -0,0 +1,109 @@
+/*
+ * 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.unomi.rest.endpoints;
+
+import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.services.SchemaRegistry;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jws.WebMethod;
+import javax.jws.WebService;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Base64;
+import java.util.List;
+
+@WebService
+@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
+@CrossOriginResourceSharing(allowAllOrigins = true, allowCredentials = true)
+@Path("/jsonSchema")
+@Component(service = JsonSchemaEndPoint.class, property = "osgi.jaxrs.resource=true")
+public class JsonSchemaEndPoint {
+
+ private static final Logger logger = LoggerFactory.getLogger(JsonSchemaEndPoint.class.getName());
+
+ @Reference
+ private SchemaRegistry schemaRegistry;
+
+ public JsonSchemaEndPoint() {
+ logger.info("Initializing JSON schema service endpoint...");
+ }
+
+ @WebMethod(exclude = true)
+ public void setSchemaRegistry(SchemaRegistry schemaRegistry) {
+ this.schemaRegistry = schemaRegistry;
+ }
+
+ /**
+ * Retrieves the 50 first json schema metadatas by default.
+ *
+ * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements
+ * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved
+ * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering
+ * elements according to the property order in the
+ * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by
+ * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}.
+ * @return a List of the 50 first json schema metadata
+ */
+ @GET
+ @Path("/")
+ public List<Metadata> getJsonSchemaMetadatas(@QueryParam("offset") @DefaultValue("0") int offset,
+ @QueryParam("size") @DefaultValue("50") int size, @QueryParam("sort") String sortBy) {
+ return schemaRegistry.getJsonSchemaMetadatas(offset, size, sortBy).getList();
+ }
+
+ /**
+ * Save a JSON schema
+ *
+ * @param jsonSchema the schema as string to save
+ * @return Response of the API call
+ */
+ @POST
+ @Path("/")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response save(String jsonSchema) {
+ schemaRegistry.saveSchema(jsonSchema);
+ return Response.ok().build();
+ }
+
+ /**
+ * Deletes a JSON schema.
+ * The id is a Base64 id as the id have is basically an URL
+ *
+ * @param base64JsonSchemaId the identifier of the JSON schema that we want to delete
+ */
+ @DELETE
+ @Path("/{base64JsonSchemaId}")
+ public void remove(@PathParam("base64JsonSchemaId") String base64JsonSchemaId) {
+ schemaRegistry.deleteSchema(new String(Base64.getDecoder().decode(base64JsonSchemaId)));
+ }
+}
diff --git a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
index be5ac44..ddaefea 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
@@ -115,7 +115,6 @@ public class EventServiceImpl implements EventService {
public void setSchemaRegistry(SchemaRegistry schemaRegistry) {
this.schemaRegistry = schemaRegistry;
}
-
public void setPersistenceService(PersistenceService persistenceService) {
this.persistenceService = persistenceService;
}
@@ -140,7 +139,7 @@ public class EventServiceImpl implements EventService {
}
public boolean isEventValid(Event event) {
- return this.schemaRegistry.isValid(event, "https://unomi.apache.org/schemas/json/events/" + event.getEventType() + "/1-0-0");
+ return schemaRegistry.isValid(event, "https://unomi.apache.org/schemas/json/events/" + event.getEventType() + "/1-0-0");
}
public String authenticateThirdPartyServer(String key, String ip) {
diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java
index eb137e0..ae4e3db 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java
@@ -20,101 +20,106 @@ package org.apache.unomi.services.impl.schemas;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.*;
+import com.networknt.schema.JsonMetaSchema;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.NonValidationKeyword;
+import com.networknt.schema.SpecVersion;
+import com.networknt.schema.ValidationMessage;
import com.networknt.schema.uri.URIFetcher;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.schema.UnomiJSONSchema;
import org.apache.unomi.api.schema.json.JSONSchema;
import org.apache.unomi.api.schema.json.JSONTypeFactory;
import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.api.services.SchedulerService;
import org.apache.unomi.api.services.SchemaRegistry;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
-import org.osgi.framework.Bundle;
+import org.apache.unomi.persistence.spi.PersistenceService;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.SynchronousBundleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.io.InputStream;
-import java.net.URL;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TimerTask;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-public class SchemaRegistryImpl implements SchemaRegistry, SynchronousBundleListener {
+public class SchemaRegistryImpl implements SchemaRegistry {
private static final String URI = "https://json-schema.org/draft/2019-09/schema";
private static final Logger logger = LoggerFactory.getLogger(SchemaRegistryImpl.class.getName());
- private final Map<Long, List<JSONSchema>> schemaTypesByBundle = new HashMap<>();
- private final Map<String, JSONSchema> schemaTypesById = new HashMap<>();
+ private final Map<String, JSONSchema> predefinedUnomiJSONSchemaById = new HashMap<>();
- private final Map<String, JsonSchema> jsonSchemasById = new LinkedHashMap<>();
-
- private final Map<String, Long> bundleIdBySchemaId = new HashMap<>();
+ private Map<String, JSONSchema> schemasById = new HashMap<>();
private BundleContext bundleContext;
private ProfileService profileService;
- private JsonSchemaFactory jsonSchemaFactory;
-
- ObjectMapper objectMapper = new ObjectMapper();
+ private PersistenceService persistenceService;
- Pattern uriPathPattern = Pattern.compile("/schemas/json(.*)/\\d-\\d-\\d");
+ private SchedulerService schedulerService;
- public void bundleChanged(BundleEvent event) {
- switch (event.getType()) {
- case BundleEvent.STARTED:
- processBundleStartup(event.getBundle().getBundleContext());
- break;
- case BundleEvent.STOPPING:
- processBundleStop(event.getBundle().getBundleContext());
- break;
- }
- }
-
- public void init() {
+ private JsonSchemaFactory jsonSchemaFactory;
- JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(URI, JsonMetaSchema.getV201909())
- .addKeyword(new UnomiPropertyTypeKeyword(profileService, this))
- .addKeyword(new NonValidationKeyword("self"))
- .build();
- jsonSchemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .addMetaSchema(jsonMetaSchema)
- .defaultMetaSchemaURI(URI)
- .uriFetcher(getBundleUriFetcher(bundleContext), "https", "http").build();
+ ObjectMapper objectMapper = new ObjectMapper();
- processBundleStartup(bundleContext);
+ private ScheduledFuture<?> scheduledFuture;
- // process already started bundles
- for (Bundle bundle : bundleContext.getBundles()) {
- if (bundle.getBundleContext() != null && bundle.getBundleId() != bundleContext.getBundle().getBundleId()) {
- processBundleStartup(bundle.getBundleContext());
- }
- }
+ private Integer jsonSchemaRefreshInterval = 1000;
- bundleContext.addBundleListener(this);
- logger.info("Schema registry initialized.");
+ public void setPersistenceService(PersistenceService persistenceService) {
+ this.persistenceService = persistenceService;
}
- public void destroy() {
- bundleContext.removeBundleListener(this);
- logger.info("Schema registry shutdown.");
+ public void setSchedulerService(SchedulerService schedulerService) {
+ this.schedulerService = schedulerService;
}
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
+ public void setJsonSchemaRefreshInterval(Integer jsonSchemaRefreshInterval) {
+ this.jsonSchemaRefreshInterval = jsonSchemaRefreshInterval;
}
- public void setProfileService(ProfileService profileService) {
- this.profileService = profileService;
+ @Override
+ public PartialList<Metadata> getJsonSchemaMetadatas(int offset, int size, String sortBy) {
+ PartialList<UnomiJSONSchema> items = persistenceService.getAllItems(UnomiJSONSchema.class, offset, size, sortBy);
+ List<Metadata> details = new LinkedList<>();
+ for (UnomiJSONSchema definition : items.getList()) {
+ details.add(definition.getMetadata());
+ }
+ return new PartialList<>(details, items.getOffset(), items.getPageSize(), items.getTotalSize(), items.getTotalSizeRelation());
}
+ @Override
public boolean isValid(Object object, String schemaId) {
- JsonSchema jsonSchema = jsonSchemasById.get(schemaId);
+ String schemaAsString;
+ JsonSchema jsonSchema = null;
+ try {
+ JSONSchema validationSchema = schemasById.get(schemaId);
+ if (validationSchema != null){
+ schemaAsString = objectMapper.writeValueAsString(schemasById.get(schemaId).getSchemaTree());
+ jsonSchema = jsonSchemaFactory.getSchema(schemaAsString);
+ } else {
+ logger.warn("No schema found for {}", schemaId);
+ }
+ } catch (JsonProcessingException e) {
+ logger.error("Failed to process json schema", e);
+ }
+
if (jsonSchema != null) {
JsonNode jsonNode = CustomObjectMapper.getObjectMapper().convertValue(object, JsonNode.class);
Set<ValidationMessage> validationMessages = jsonSchema.validate(jsonNode);
@@ -130,128 +135,142 @@ public class SchemaRegistryImpl implements SchemaRegistry, SynchronousBundleList
}
@Override
- public List<JSONSchema> getTargetSchemas(String target) {
- return schemaTypesById.values().stream().filter(jsonSchema -> jsonSchema.getTarget() != null && jsonSchema.getTarget().equals(target)).collect(Collectors.toList());
+ public List<JSONSchema> getSchemasByTarget(String target) {
+ return schemasById.values().stream().filter(jsonSchema -> jsonSchema.getTarget() != null && jsonSchema.getTarget().equals(target))
+ .collect(Collectors.toList());
+
}
@Override
- public JSONSchema getSchema(String schemaId) {
- return schemaTypesById.get(schemaId);
+ public void saveSchema(String schema) {
+ JsonSchema jsonSchema = jsonSchemaFactory.getSchema(schema);
+ if (predefinedUnomiJSONSchemaById.get(jsonSchema.getSchemaNode().get("$id").asText()) == null) {
+ persistenceService.save(buildUnomiJsonSchema(schema));
+ JSONSchema localSchema = buildJSONSchema(jsonSchema);
+ schemasById.put(jsonSchema.getSchemaNode().get("$id").asText(), localSchema);
+ } else {
+ logger.error("Can not store a JSON Schema which have the id of a schema preovided by Unomi");
+ }
}
- private void loadPredefinedSchemas(BundleContext bundleContext) {
- Enumeration<URL> predefinedSchemas = bundleContext.getBundle().findEntries("META-INF/cxs/schemas", "*.json", true);
- if (predefinedSchemas == null) {
- return;
- }
+ @Override
+ public void saveSchema(InputStream schemaStream) throws IOException {
+ saveSchema(IOUtils.toString(schemaStream));
+ }
- List<JSONSchema> jsonSchemas = this.schemaTypesByBundle.get(bundleContext.getBundle().getBundleId());
-
- while (predefinedSchemas.hasMoreElements()) {
- URL predefinedSchemaURL = predefinedSchemas.nextElement();
- logger.debug("Found predefined JSON schema at " + predefinedSchemaURL + ", loading... ");
-
- try (InputStream schemaInputStream = predefinedSchemaURL.openStream()) {
- JsonSchema jsonSchema = jsonSchemaFactory.getSchema(schemaInputStream);
- String schemaTarget = null;
- String[] splitPath = predefinedSchemaURL.getPath().split("/");
- if (splitPath.length > 5) {
- String target = splitPath[4];
- if (StringUtils.isNotBlank(target)) {
- schemaTarget = target;
- }
- }
- registerSchema(bundleContext.getBundle().getBundleId(), jsonSchemas, schemaTarget, jsonSchema);
- } catch (Exception e) {
- logger.error("Error while loading schema definition " + predefinedSchemaURL, e);
- }
- }
+ @Override
+ public void loadPredefinedSchema(InputStream schemaStream) {
+ JsonSchema jsonSchema = jsonSchemaFactory.getSchema(schemaStream);
+ JSONSchema localJsonSchema = buildJSONSchema(jsonSchema);
+ predefinedUnomiJSONSchemaById.put(jsonSchema.getSchemaNode().get("$id").asText(), localJsonSchema);
+ schemasById.put(jsonSchema.getSchemaNode().get("$id").asText(), localJsonSchema);
}
- public String registerSchema(String target, InputStream jsonSchemaInputStream) {
- JsonSchema jsonSchema = jsonSchemaFactory.getSchema(jsonSchemaInputStream);
- try {
- return registerSchema(null, null, target, jsonSchema);
- } catch (JsonProcessingException e) {
- logger.error("Error registering JSON schema", e);
- return null;
- }
+ @Override
+ public boolean deleteSchema(String schemaId) {
+ schemasById.remove(schemaId);
+ return persistenceService.remove(schemaId, UnomiJSONSchema.class);
}
- public boolean unregisterSchema(String target, String schemaId) {
- jsonSchemasById.remove(schemaId);
- schemaTypesById.remove(schemaId);
- return true;
+ @Override
+ public boolean deleteSchema(InputStream schemaStream) {
+ JsonNode schemaNode = jsonSchemaFactory.getSchema(schemaStream).getSchemaNode();
+ return deleteSchema(schemaNode.get("$id").asText());
}
- private String registerSchema(Long bundleId, List<JSONSchema> jsonSchemas, String target, JsonSchema jsonSchema) throws JsonProcessingException {
- String schemaId = jsonSchema.getSchemaNode().get("$id").asText();
- jsonSchemasById.put(schemaId, jsonSchema);
- if (bundleContext != null) {
- bundleIdBySchemaId.put(schemaId, bundleId);
- }
- Map<String, Object> schemaTree = (Map<String, Object>) objectMapper.treeToValue(jsonSchema.getSchemaNode(), Map.class);
- JSONSchema unomiJsonSchema = new JSONSchema(schemaTree, new JSONTypeFactory(this), this);
- if (bundleId != null) {
- unomiJsonSchema.setPluginId(bundleId);
- }
- unomiJsonSchema.setSchemaId(schemaId);
- unomiJsonSchema.setTarget(target);
- if (jsonSchemas != null) {
- jsonSchemas.add(unomiJsonSchema);
+ @Override
+ public JSONSchema getSchema(String schemaId) {
+ return schemasById.get(schemaId);
+ }
+
+ private JSONSchema buildJSONSchema(JsonSchema jsonSchema) {
+ return Optional.of(jsonSchema).map(jsonSchemaToProcess -> {
+ try {
+ return (Map<String, Object>) objectMapper.treeToValue(jsonSchemaToProcess.getSchemaNode(), Map.class);
+ } catch (JsonProcessingException e) {
+ logger.error("Failed to process Json object, e");
+ }
+ return Collections.<String, Object>emptyMap();
+ }).map(jsonSchemaToProcess -> {
+ JSONSchema schema = new JSONSchema(jsonSchemaToProcess, new JSONTypeFactory(this));
+ schema.setPluginId(bundleContext.getBundle().getBundleId());
+ return schema;
+ }).get();
+ }
+
+ private UnomiJSONSchema buildUnomiJsonSchema(String schema) {
+ JsonNode schemaNode = jsonSchemaFactory.getSchema(schema).getSchemaNode();
+ return new UnomiJSONSchema(schemaNode.get("$id").asText(), schema, schemaNode.at("/self/target").asText());
+ }
+
+ public JsonSchema getJsonSchema(String schemaId) {
+ String schemaAsString = null;
+ try {
+ schemaAsString = objectMapper.writeValueAsString(schemasById.get(schemaId).getSchemaTree());
+ } catch (JsonProcessingException e) {
+ logger.error("Failed to process json schema", e);
}
- schemaTypesById.put(schemaId, unomiJsonSchema);
- return schemaId;
+ return jsonSchemaFactory.getSchema(schemaAsString);
}
- private URIFetcher getBundleUriFetcher(BundleContext bundleContext) {
+ private URIFetcher getUriFetcher() {
return uri -> {
logger.debug("Fetching schema {}", uri);
- Long bundleId = bundleIdBySchemaId.get(uri.toString());
- if (bundleId == null) {
- logger.error("Couldn't find bundle for schema {}", uri);
- return null;
- }
- Matcher uriPathMatcher = uriPathPattern.matcher(uri.getPath());
- String uriPath = uri.getPath();
- if (uriPathMatcher.matches()) {
- uriPath = uriPathMatcher.group(1) + ".json";
+ String schemaAsString = null;
+ try {
+ schemaAsString = objectMapper.writeValueAsString(schemasById.get(uri.toString()).getSchemaTree());
+ } catch (JsonProcessingException e) {
+ logger.error("Failed to process json schema", e);
}
- URL schemaURL = bundleContext.getBundle(bundleId).getResource("META-INF/cxs/schemas" + uriPath);
- if (schemaURL != null) {
- return schemaURL.openStream();
- } else {
- logger.error("Couldn't find resource {} in bundle {}", "META-INF/cxs/schemas" + uriPath, bundleId);
+ JsonSchema schema = jsonSchemaFactory.getSchema(schemaAsString);
+ if (schema == null) {
+ logger.error("Couldn't find schema {}", uri);
return null;
}
+ return IOUtils.toInputStream(schema.getSchemaNode().asText());
};
}
- private void processBundleStartup(BundleContext bundleContext) {
- if (bundleContext == null) {
- return;
- }
- schemaTypesByBundle.put(bundleContext.getBundle().getBundleId(), new ArrayList<>());
- loadPredefinedSchemas(bundleContext);
+ private void refreshJSONSchemas() {
+ schemasById = new HashMap<>();
+ schemasById.putAll(predefinedUnomiJSONSchemaById);
+ persistenceService.getAllItems(UnomiJSONSchema.class).forEach(
+ jsonSchema -> schemasById.put(jsonSchema.getId(), buildJSONSchema(jsonSchemaFactory.getSchema(jsonSchema.getSchema()))));
}
- private void processBundleStop(BundleContext bundleContext) {
- if (bundleContext == null) {
- return;
- }
- List<JSONSchema> JSONSchemas = schemaTypesByBundle.remove(bundleContext.getBundle().getBundleId());
- if (JSONSchemas != null) {
- for (JSONSchema JSONSchema : JSONSchemas) {
- jsonSchemasById.remove(JSONSchema.getSchemaId());
- bundleIdBySchemaId.remove(JSONSchema.getSchemaId());
- schemaTypesById.remove(JSONSchema.getSchemaId());
+ private void initializeTimers() {
+ TimerTask task = new TimerTask() {
+ @Override
+ public void run() {
+ refreshJSONSchemas();
}
- }
+ };
+ scheduledFuture = schedulerService.getScheduleExecutorService()
+ .scheduleWithFixedDelay(task, 0, jsonSchemaRefreshInterval, TimeUnit.MILLISECONDS);
+ }
+
+ public void init() {
+
+ JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(URI, JsonMetaSchema.getV201909())
+ .addKeyword(new UnomiPropertyTypeKeyword(profileService, this)).addKeyword(new NonValidationKeyword("self")).build();
+ jsonSchemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
+ .addMetaSchema(jsonMetaSchema).defaultMetaSchemaURI(URI).uriFetcher(getUriFetcher(), "https", "http").build();
+
+ initializeTimers();
+ logger.info("Schema registry initialized.");
+ }
+
+ public void destroy() {
+ scheduledFuture.cancel(true);
+ logger.info("Schema registry shutdown.");
}
- protected JsonSchema getJsonSchema(String schemaId) {
- return jsonSchemasById.get(schemaId);
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
}
+ public void setProfileService(ProfileService profileService) {
+ this.profileService = profileService;
+ }
}
diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/UnomiPropertyTypeKeyword.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/UnomiPropertyTypeKeyword.java
index 2375e7b..2f02d51 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/schemas/UnomiPropertyTypeKeyword.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/UnomiPropertyTypeKeyword.java
@@ -17,19 +17,29 @@
package org.apache.unomi.services.impl.schemas;
import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.*;
+import com.networknt.schema.AbstractJsonValidator;
+import com.networknt.schema.AbstractKeyword;
+import com.networknt.schema.CustomErrorMessageType;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaException;
+import com.networknt.schema.JsonValidator;
+import com.networknt.schema.ValidationContext;
+import com.networknt.schema.ValidationMessage;
import org.apache.unomi.api.PropertyType;
import org.apache.unomi.api.services.ProfileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
-import java.util.*;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
class UnomiPropertyTypeKeyword extends AbstractKeyword {
- private static final Logger logger = LoggerFactory.getLogger(UnomiPropertyTypeKeyword.class);
-
private final ProfileService profileService;
private final SchemaRegistryImpl schemaRegistry;
@@ -42,7 +52,8 @@ class UnomiPropertyTypeKeyword extends AbstractKeyword {
ProfileService profileService;
SchemaRegistryImpl schemaRegistry;
- public UnomiPropertyTypeJsonValidator(String keyword, String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, ProfileService profileService, SchemaRegistryImpl schemaRegistry) {
+ public UnomiPropertyTypeJsonValidator(String keyword, String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
+ ValidationContext validationContext, ProfileService profileService, SchemaRegistryImpl schemaRegistry) {
super(keyword);
this.schemaPath = schemaPath;
this.schemaNode = schemaNode;
@@ -60,13 +71,17 @@ class UnomiPropertyTypeKeyword extends AbstractKeyword {
String fieldName = fieldNames.next();
PropertyType propertyType = getPropertyType(fieldName);
if (propertyType == null) {
- validationMessages.add(buildValidationMessage(CustomErrorMessageType.of("property-not-found", new MessageFormat("{0} : Couldn''t find property type with id={1}")), at, fieldName));
+ validationMessages.add(buildValidationMessage(CustomErrorMessageType
+ .of("property-not-found", new MessageFormat("{0} : Couldn''t find property type with id={1}")), at, fieldName));
} else {
// @todo further validation, if it can be used in this context (event, profile, session)
String valueTypeId = propertyType.getValueTypeId();
- JsonSchema jsonSchema = schemaRegistry.getJsonSchema("https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json");
+ JsonSchema jsonSchema = schemaRegistry
+ .getJsonSchema("https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json");
if (jsonSchema == null) {
- validationMessages.add(buildValidationMessage(CustomErrorMessageType.of("value-schema-not-found", new MessageFormat("{0} : Couldn''t find schema type with id={1}")), at, "https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json"));
+ validationMessages.add(buildValidationMessage(CustomErrorMessageType
+ .of("value-schema-not-found", new MessageFormat("{0} : Couldn''t find schema type with id={1}")), at,
+ "https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json"));
} else {
Set<ValidationMessage> propertyValidationMessages = jsonSchema.validate(node.get(fieldName));
if (propertyValidationMessages != null) {
@@ -104,7 +119,9 @@ class UnomiPropertyTypeKeyword extends AbstractKeyword {
}
@Override
- public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
- return new UnomiPropertyTypeJsonValidator(this.getValue(), schemaPath, schemaNode, parentSchema, validationContext, profileService, schemaRegistry);
+ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext)
+ throws JsonSchemaException, Exception {
+ return new UnomiPropertyTypeJsonValidator(this.getValue(), schemaPath, schemaNode, parentSchema, validationContext, profileService,
+ schemaRegistry);
}
}
diff --git a/services/src/main/java/org/apache/unomi/services/listener/JsonSchemaListener.java b/services/src/main/java/org/apache/unomi/services/listener/JsonSchemaListener.java
new file mode 100644
index 0000000..b3b2fd2
--- /dev/null
+++ b/services/src/main/java/org/apache/unomi/services/listener/JsonSchemaListener.java
@@ -0,0 +1,170 @@
+/*
+ * 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.unomi.services.listener;
+
+import org.apache.unomi.api.schema.UnomiJSONSchema;
+import org.apache.unomi.api.services.SchemaRegistry;
+import org.apache.unomi.persistence.spi.PersistenceService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * An implementation of a BundleListener for the JSON schema.
+ * It will load the pre-defined schema files in the folder META-INF/cxs/schemas.
+ * The script will be stored in the ES index jsonSchemas
+ */
+public class JsonSchemaListener implements SynchronousBundleListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(JsonSchemaListener.class.getName());
+ public static final String ENTRIES_LOCATION = "META-INF/cxs/schemas";
+
+ private PersistenceService persistenceService;
+
+ private SchemaRegistry schemaRegistry;
+
+ private BundleContext bundleContext;
+
+ public void setPersistenceService(PersistenceService persistenceService) {
+ this.persistenceService = persistenceService;
+ }
+
+ public void setSchemaRegistry(SchemaRegistry schemaRegistry) {
+ this.schemaRegistry = schemaRegistry;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void postConstruct() {
+ logger.info("JSON schema listener initializing...");
+ logger.debug("postConstruct {}", bundleContext.getBundle());
+ createIndex();
+
+ loadPredefinedSchemas(bundleContext);
+
+ for (Bundle bundle : bundleContext.getBundles()) {
+ if (bundle.getBundleContext() != null && bundle.getBundleId() != bundleContext.getBundle().getBundleId()) {
+ saveSchemas(bundle.getBundleContext());
+ }
+ }
+
+ bundleContext.addBundleListener(this);
+ logger.info("JSON schema listener initialized.");
+ }
+
+ public void preDestroy() {
+ bundleContext.removeBundleListener(this);
+ logger.info("JSON schema listener shutdown.");
+ }
+
+ private void processBundleStartup(BundleContext bundleContext) {
+ if (bundleContext == null) {
+ return;
+ }
+ saveSchemas(bundleContext);
+ }
+
+ private void processBundleStop(BundleContext bundleContext) {
+ if (bundleContext == null) {
+ return;
+ }
+ unloadSchemas(bundleContext);
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ switch (event.getType()) {
+ case BundleEvent.STARTED:
+ processBundleStartup(event.getBundle().getBundleContext());
+ break;
+ case BundleEvent.STOPPING:
+ if (!event.getBundle().getSymbolicName().equals(bundleContext.getBundle().getSymbolicName())) {
+ processBundleStop(event.getBundle().getBundleContext());
+ }
+ break;
+ }
+ }
+
+ public void createIndex() {
+ if (persistenceService.createIndex(UnomiJSONSchema.ITEM_TYPE)) {
+ logger.info("{} index created", UnomiJSONSchema.ITEM_TYPE);
+ } else {
+ logger.info("{} index already exists", UnomiJSONSchema.ITEM_TYPE);
+ }
+ }
+
+ private void saveSchemas(BundleContext bundleContext) {
+ Enumeration<URL> predefinedSchemas = bundleContext.getBundle().findEntries(ENTRIES_LOCATION, "*.json", true);
+ if (predefinedSchemas == null) {
+ return;
+ }
+
+ while (predefinedSchemas.hasMoreElements()) {
+ URL predefinedSchemaURL = predefinedSchemas.nextElement();
+ logger.debug("Found JSON schema at {}, loading... ", predefinedSchemaURL);
+
+ try (InputStream schemaInputStream = predefinedSchemaURL.openStream()) {
+ schemaRegistry.saveSchema(schemaInputStream);
+ } catch (Exception e) {
+ logger.error("Error while loading schema definition {}", predefinedSchemaURL, e);
+ }
+ }
+ }
+
+ private void loadPredefinedSchemas(BundleContext bundleContext) {
+ Enumeration<URL> predefinedSchemas = bundleContext.getBundle().findEntries(ENTRIES_LOCATION, "*.json", true);
+ if (predefinedSchemas == null) {
+ return;
+ }
+
+ while (predefinedSchemas.hasMoreElements()) {
+ URL predefinedSchemaURL = predefinedSchemas.nextElement();
+ logger.debug("Found predefined JSON schema at {}, loading... ", predefinedSchemaURL);
+ try (InputStream schemaInputStream = predefinedSchemaURL.openStream()) {
+ schemaRegistry.loadPredefinedSchema(schemaInputStream);
+ } catch (Exception e) {
+ logger.error("Error while loading schema definition {}", predefinedSchemaURL, e);
+ }
+ }
+ }
+
+ private void unloadSchemas(BundleContext bundleContext) {
+ Enumeration<URL> predefinedSchemas = bundleContext.getBundle().findEntries(ENTRIES_LOCATION, "*.json", true);
+ if (predefinedSchemas == null) {
+ return;
+ }
+
+ while (predefinedSchemas.hasMoreElements()) {
+ URL predefinedSchemaURL = predefinedSchemas.nextElement();
+ logger.debug("Found predefined JSON schema at {}, loading... ", predefinedSchemaURL);
+
+ try (InputStream schemaInputStream = predefinedSchemaURL.openStream()) {
+ schemaRegistry.deleteSchema(schemaInputStream);
+ } catch (Exception e) {
+ logger.error("Error while removing schema at {}", predefinedSchemaURL, e);
+ }
+ }
+ }
+}
diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index a8fa903..4be07c9 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -45,6 +45,7 @@
<cm:property name="events.shouldBeCheckedEventSourceId" value="false"/>
<cm:property name="rules.optimizationActivated" value="true"/>
<cm:property name="schedules.thread.poolSize" value="5"/>
+ <cm:property name="json.schema.refresh.interval" value="1000"/>
</cm:default-properties>
</cm:property-placeholder>
@@ -104,16 +105,31 @@
<bean id="schemaRegistryImpl" class="org.apache.unomi.services.impl.schemas.SchemaRegistryImpl" init-method="init"
destroy-method="destroy">
<property name="bundleContext" ref="blueprintBundleContext"/>
- <property name="profileService" ref="profileServiceImpl" />
+ <property name="profileService" ref="profileServiceImpl"/>
+ <property name="persistenceService" ref="persistenceService"/>
+ <property name="schedulerService" ref="schedulerServiceImpl"/>
+ <property name="jsonSchemaRefreshInterval" value="${services.json.schema.refresh.interval}"/>
</bean>
<service id="schemaRegistry" ref="schemaRegistryImpl" interface="org.apache.unomi.api.services.SchemaRegistry"/>
+ <bean id="jsonSchemaListenerImpl" class="org.apache.unomi.services.listener.JsonSchemaListener"
+ init-method="postConstruct" destroy-method="preDestroy">
+ <property name="persistenceService" ref="persistenceService"/>
+ <property name="bundleContext" ref="blueprintBundleContext"/>
+ <property name="schemaRegistry" ref="schemaRegistryImpl"/>
+ </bean>
+ <service id="jsonSchemaListener" ref="jsonSchemaListenerImpl">
+ <interfaces>
+ <value>org.osgi.framework.SynchronousBundleListener</value>
+ </interfaces>
+ </service>
+
<bean id="eventServiceImpl" class="org.apache.unomi.services.impl.events.EventServiceImpl">
<property name="persistenceService" ref="persistenceService"/>
<property name="definitionsService" ref="definitionsServiceImpl"/>
<property name="sourceService" ref="sourceServiceImpl"/>
<property name="bundleContext" ref="blueprintBundleContext"/>
- <property name="schemaRegistry" ref="schemaRegistryImpl" />
+ <property name="schemaRegistry" ref="schemaRegistryImpl"/>
<property name="predefinedEventTypeIds">
<set>
<value>view</value>
diff --git a/services/src/main/resources/org.apache.unomi.services.cfg b/services/src/main/resources/org.apache.unomi.services.cfg
index fca3cc9..1f9a9bd 100644
--- a/services/src/main/resources/org.apache.unomi.services.cfg
+++ b/services/src/main/resources/org.apache.unomi.services.cfg
@@ -76,4 +76,7 @@ events.shouldBeCheckedEventSourceId=${org.apache.unomi.events.shouldBeCheckedEve
rules.optimizationActivated=${org.apache.unomi.rules.optimizationActivated:-true}
# The number of threads to compose the pool size of the scheduler.
-scheduler.thread.poolSize=${org.apache.unomi.scheduler.thread.poolSize:-5}
\ No newline at end of file
+scheduler.thread.poolSize=${org.apache.unomi.scheduler.thread.poolSize:-5}
+
+# The interval in milliseconds to reload the json schemas in memory
+services.json.schema.refresh.interval=${org.apache.unomi.json.schema.refresh.interval:-1000}
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java
index e417d0d..56db909 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java
@@ -73,6 +73,7 @@ public abstract class DeploymentCommandSupport implements Action {
public static final String VALUE_DEFINITION_TYPE = "values";
public static final String MERGER_DEFINITION_TYPE = "mergers";
public static final String MAPPING_DEFINITION_TYPE = "mappings";
+ public static final String JSON_SCHEMA_DEFINITION_TYPE = "jsonschema";
protected final static List<String> definitionTypes = Arrays.asList(
CONDITION_DEFINITION_TYPE,
@@ -87,7 +88,8 @@ public abstract class DeploymentCommandSupport implements Action {
PATCH_DEFINITION_TYPE,
VALUE_DEFINITION_TYPE,
MERGER_DEFINITION_TYPE,
- MAPPING_DEFINITION_TYPE);
+ MAPPING_DEFINITION_TYPE,
+ JSON_SCHEMA_DEFINITION_TYPE);
@Argument(index = 0, name = "bundleId", description = "The bundle identifier where to find the definition", multiValued = false)
Long bundleIdentifier;