aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openecomp/schema
diff options
context:
space:
mode:
authorShwetank Dave <shwetank.dave@amdocs.com>2017-08-02 17:10:38 -0400
committerShwetank Dave <shwetank.dave@amdocs.com>2017-08-03 11:28:39 -0400
commite9c94429a8ada3e55854515060df817945b73d87 (patch)
tree3c5a9ac766d99570d542c1996f1321c99e5498e5 /src/main/java/org/openecomp/schema
parent27ff75c77611a8bdae37c7c0092787938c63a786 (diff)
[AAI-26] Adding gizmo data to the repository.
Change-Id: I183f837d45acbfe3c673fde1acf8768d5e3fd37b Signed-off-by: Shwetank Dave <shwetank.dave@amdocs.com>
Diffstat (limited to 'src/main/java/org/openecomp/schema')
-rw-r--r--src/main/java/org/openecomp/schema/OxmModelLoader.java168
-rw-r--r--src/main/java/org/openecomp/schema/OxmModelValidator.java325
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchema.java138
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java158
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java341
5 files changed, 1130 insertions, 0 deletions
diff --git a/src/main/java/org/openecomp/schema/OxmModelLoader.java b/src/main/java/org/openecomp/schema/OxmModelLoader.java
new file mode 100644
index 0000000..4ef77a2
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/OxmModelLoader.java
@@ -0,0 +1,168 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import org.eclipse.persistence.jaxb.JAXBContextProperties;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class OxmModelLoader {
+
+ private static Map<String, DynamicJAXBContext> versionContextMap
+ = new ConcurrentHashMap<String, DynamicJAXBContext>();
+ private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+ final static Pattern p = Pattern.compile("aai_oxm_(.*).xml");
+
+ private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+ .getLogger(OxmModelLoader.class.getName());
+
+ public synchronized static void loadModels() throws CrudException {
+ ClassLoader cl = OxmModelLoader.class.getClassLoader();
+ ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
+ Resource[] resources;
+ try {
+ resources = resolver.getResources("classpath*:/oxm/aai_oxm*.xml");
+ } catch (IOException ex) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, ex.getMessage());
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ if (resources.length == 0) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "No OXM schema files found on classpath");
+ throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+ }
+
+ for (Resource resource : resources) {
+ Matcher matcher = p.matcher(resource.getFilename());
+
+ if (matcher.matches()) {
+ try {
+ OxmModelLoader.loadModel(matcher.group(1), resource);
+ } catch (Exception e) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "Failed to load " + resource.getFilename()
+ + ": " + e.getMessage());
+ throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+ }
+ }
+ }
+ }
+
+ private static void addtimer(String version, File file) {
+ TimerTask task = null;
+ task = new FileWatcher(
+ file) {
+ protected void onChange(File file) {
+ // here we implement the onChange
+ logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+ try {
+ OxmModelLoader.loadModel(version, file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ if (!timers.containsKey(version)) {
+ Timer timer = new Timer("oxm-" + version);
+ timer.schedule(task, new Date(), 10000);
+ timers.put(version, timer);
+
+ }
+ }
+
+ private synchronized static void loadModel(String version, File file)
+ throws JAXBException, IOException {
+ InputStream inputStream = new FileInputStream(file);
+ loadModel(version, file.getName(), inputStream);
+ addtimer(version, file);
+ }
+
+ private synchronized static void loadModel(String version, Resource resource)
+ throws JAXBException, IOException {
+ InputStream inputStream = resource.getInputStream();
+ loadModel(version, resource.getFilename(), inputStream);
+ }
+
+ private synchronized static void loadModel(String version, String resourceName,
+ InputStream inputStream)
+ throws JAXBException, IOException {
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStream);
+ final DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory
+ .createContextFromOXM(Thread.currentThread().getContextClassLoader(), properties);
+ versionContextMap.put(version, jaxbContext);
+ logger.info(CrudServiceMsgs.LOADED_OXM_FILE, resourceName);
+ }
+
+ public static DynamicJAXBContext getContextForVersion(String version) throws CrudException {
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ try {
+ loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_oxm_"
+ + version + ".xml"));
+ } catch (Exception e) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ public static Map<String, DynamicJAXBContext> getVersionContextMap() {
+ return versionContextMap;
+ }
+
+ public static void setVersionContextMap(Map<String, DynamicJAXBContext> versionContextMap) {
+ OxmModelLoader.versionContextMap = versionContextMap;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/schema/OxmModelValidator.java b/src/main/java/org/openecomp/schema/OxmModelValidator.java
new file mode 100644
index 0000000..45a2597
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/OxmModelValidator.java
@@ -0,0 +1,325 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.eclipse.persistence.dynamic.DynamicType;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+import org.eclipse.persistence.oxm.XMLField;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.util.CrudServiceUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class OxmModelValidator {
+ public enum Metadata {
+ NODE_TYPE("aai-node-type"),
+ URI("aai-uri"),
+ CREATED_TS("aai-created-ts"),
+ SOT("source-of-truth"),
+ LAST_MOD_SOT("last-mod-source-of-truth");
+
+ private final String propName;
+
+ Metadata(String propName) {
+ this.propName = propName;
+ }
+
+ public String propertyName() {
+ return propName;
+ }
+
+ public static boolean isProperty(String property) {
+ for (Metadata meta : Metadata.values()) {
+ if (meta.propName.equals(property)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+
+ public static Map<String, Object> resolveCollectionfilter(String version, String type,
+ Map<String, String> filter)
+ throws CrudException {
+
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (jaxbContext == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+ .to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+ for (String key : filter.keySet()) {
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key);
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) {
+ try {
+ DatabaseMapping mapping = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName);
+ Object value = CrudServiceUtil.validateFieldType(filter.get(key),
+ mapping.getField().getType());
+ result.put(key, value);
+ } catch (Exception ex) {
+ // Skip any exceptions thrown while validating the filter
+ // key value
+ continue;
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ public static String resolveCollectionType(String version, String type) throws CrudException {
+
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+ if (jaxbContext == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ // Determine if the Object part is a collection type in the model
+ // definition
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+ .to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+ if (modelObjectType == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ if (modelObjectType.getDescriptor().getMappings().size() == 1
+ && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) {
+ String childJavaObjectName = modelObjectType.getDescriptor().getMappings()
+ .get(0).getAttributeName();
+ childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName);
+ final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName);
+ if (childObjectType == null) {
+ // Should not happen as child object is defined in oxm model
+ // itself
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ return childObjectType.getDescriptor().getTableName();
+ } else {
+ return modelObjectType.getDescriptor().getTableName();
+ }
+
+ }
+
+
+ public static Vertex validateIncomingUpsertPayload(String id, String version, String type,
+ JsonElement properties)
+ throws CrudException {
+
+ try {
+ type = resolveCollectionType(version, type);
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+ .entrySet();
+
+ //loop through input to validate against schema
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+ // check for valid field
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+ throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ Map<String, JsonElement> entriesMap = new HashMap<String, JsonElement>();
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+ entriesMap.put(entry.getKey(), entry.getValue());
+ }
+
+ Vertex.Builder modelVertexBuilder = new Vertex.Builder(type);
+ if (id != null) {
+ modelVertexBuilder.id(id);
+ }
+ for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
+ if (mapping.isAbstractDirectMapping()) {
+ DatabaseField field = mapping.getField();
+ String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
+ : mapping.getProperties().get("defaultValue").toString();
+
+ String keyName = field.getName().substring(0, field.getName().indexOf("/"));
+
+ if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+ && !defaultValue.isEmpty()) {
+ modelVertexBuilder.property(keyName,
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ }
+ // if schema field is required and not set then reject
+ if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+ && defaultValue.isEmpty()) {
+ throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST);
+ }
+ // If invalid field then reject
+ if (entriesMap.containsKey(keyName)) {
+ Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName)
+ .getAsString(), field.getType());
+ modelVertexBuilder.property(keyName, value);
+ }
+
+ // Set defaults
+ if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) {
+ modelVertexBuilder.property(keyName,
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ }
+ }
+ }
+
+ return modelVertexBuilder.build();
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+ public static Vertex validateIncomingPatchPayload(String id, String version, String type,
+ JsonElement properties, Vertex existingVertex)
+ throws CrudException {
+
+ try {
+ type = resolveCollectionType(version, type);
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+ .entrySet();
+
+ // Loop through the payload properties and merge with existing
+ // vertex props
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+ // check for valid field
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+ throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+ }
+
+ DatabaseField field = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName).getField();
+ String defaultValue = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName)
+ .getProperties().get("defaultValue") == null ? ""
+ : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName)
+ .getProperties().get("defaultValue").toString();
+
+ // check if mandatory field is not set to null
+ if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && !defaultValue.isEmpty()) {
+ existingVertex.getProperties().put(entry.getKey(),
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && defaultValue.isEmpty()) {
+ throw new CrudException("Mandatory field: " + entry.getKey()
+ + " can't be set to null",
+ Status.BAD_REQUEST);
+ } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && existingVertex.getProperties().containsKey(entry.getKey())) {
+ existingVertex.getProperties().remove(entry.getKey());
+ } else if (!(entry.getValue() instanceof JsonNull)) {
+ // add/update the value if found in existing vertex
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ field.getType());
+ existingVertex.getProperties().put(entry.getKey(), value);
+ }
+
+ }
+
+ return existingVertex;
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) {
+ for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) {
+ int ix = field.getName().indexOf("/");
+ if (ix <= 0) {
+ ix = field.getName().length();
+ }
+
+ String keyName = field.getName().substring(0, ix);
+ if (fieldName.equals(keyName)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ public static Vertex validateOutgoingPayload(String version, Vertex vertex) {
+
+ Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType())
+ .id(vertex.getId().get());
+
+ try {
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL,
+ vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null
+ ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType()));
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ for (String key : vertex.getProperties().keySet()) {
+ DatabaseField field = getDatabaseField(key, modelObjectType);
+ if (field != null) {
+ if (!Metadata.isProperty(key)) {
+ modelVertexBuilder.property(key, vertex.getProperties().get(key));
+ }
+ }
+ }
+ return modelVertexBuilder.build();
+ } catch (Exception ex) {
+ return vertex;
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchema.java b/src/main/java/org/openecomp/schema/RelationshipSchema.java
new file mode 100644
index 0000000..5b28e28
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchema.java
@@ -0,0 +1,138 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchema {
+ private static final Gson gson = new GsonBuilder().create();
+
+ public static final String SCHEMA_SOURCE_NODE_TYPE = "source-node-type";
+ public static final String SCHEMA_TARGET_NODE_TYPE = "target-node-type";
+ public static final String SCHEMA_RELATIONSHIP_TYPE = "relationship-type";
+ public static final String SCHEMA_RELATIONSHIP_TYPES_ARRAY = "relationship-types";
+ public static final String SCHEMA_RELATIONSHIP_PROPERTIES = "properties";
+ public static final String SCHEMA_RELATIONS_ARRAY = "relations";
+
+ /**
+ * key = source-node-type:target-node-type:relationship-type value = map of properties with name
+ * and type . Like propertyName:PropertyType
+ */
+ private HashMap<String, HashMap<String, Class<?>>> relations
+ = new HashMap<String, HashMap<String, Class<?>>>();
+ /**
+ * Hashmap of valid relationship types alongwith properrties.
+ */
+ private HashMap<String, HashMap<String, Class<?>>> relationTypes
+ = new HashMap<String, HashMap<String, Class<?>>>();
+
+
+ public RelationshipSchema(String json) throws CrudException {
+
+ JsonParser parser = new JsonParser();
+ try {
+ JsonObject root = parser.parse(json).getAsJsonObject();
+ JsonArray relationshipTypesArray = root.getAsJsonArray(SCHEMA_RELATIONSHIP_TYPES_ARRAY);
+ JsonArray relationsArray = root.getAsJsonArray(SCHEMA_RELATIONS_ARRAY);
+
+ //First load all the relationship-types
+ for (JsonElement item : relationshipTypesArray) {
+ JsonObject obj = item.getAsJsonObject();
+ String type = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+
+
+ HashMap<String, Class<?>> props = new HashMap<String, Class<?>>();
+ Set<Map.Entry<String, JsonElement>> entries = obj.get(SCHEMA_RELATIONSHIP_PROPERTIES)
+ .getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+ props.put(entry.getKey(), resolveClass(entry.getValue().getAsString()));
+
+ }
+ relationTypes.put(type, props);
+
+ }
+
+ for (JsonElement item : relationsArray) {
+ JsonObject obj = item.getAsJsonObject();
+ // Parse the Source/Taget nodeTypes
+
+ String relationType = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+ String key = obj.get(SCHEMA_SOURCE_NODE_TYPE).getAsString() + ":"
+ + obj.get(SCHEMA_TARGET_NODE_TYPE).getAsString() + ":" + relationType;
+
+
+ if (!relationTypes.containsKey(relationType)) {
+ throw new CrudException(SCHEMA_RELATIONSHIP_TYPE + ": " + relationType + " not found",
+ Status.BAD_REQUEST);
+ }
+
+ relations.put(key, relationTypes.get(relationType));
+ }
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+
+ public HashMap<String, Class<?>> lookupRelation(String key) {
+ return this.relations.get(key);
+ }
+
+ public HashMap<String, Class<?>> lookupRelationType(String type) {
+ return this.relationTypes.get(type);
+ }
+
+ public boolean isValidType(String type) {
+ return relationTypes.containsKey(type);
+ }
+
+ private Class<?> resolveClass(String type) throws CrudException, ClassNotFoundException {
+ Class<?> clazz = Class.forName(type);
+ validateClassTypes(clazz);
+ return clazz;
+ }
+
+ private void validateClassTypes(Class<?> clazz) throws CrudException {
+ if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class)
+ && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) {
+ throw new CrudException("", Status.BAD_REQUEST);
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java
new file mode 100644
index 0000000..51b33d0
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java
@@ -0,0 +1,158 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import org.apache.commons.io.IOUtils;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class RelationshipSchemaLoader {
+
+ private static Map<String, RelationshipSchema> versionContextMap
+ = new ConcurrentHashMap<String, RelationshipSchema>();
+ private static SortedSet<Integer> versions = new TreeSet<Integer>();
+ private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+ final static Pattern filePattern = Pattern.compile("aai_relationship_(.*).json");
+
+
+ private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+ .getLogger(RelationshipSchemaLoader.class.getName());
+
+ public synchronized static void loadModels() {
+
+ File[] listOfFiles = new File(CrudServiceConstants.CRD_HOME_MODEL).listFiles();
+
+ if (listOfFiles != null) {
+ for (File file : listOfFiles) {
+ if (file.isFile()) {
+ Matcher matcher = filePattern.matcher(file.getName());
+ if (matcher.matches()) {
+ try {
+ RelationshipSchemaLoader.loadModel(matcher.group(1), file);
+ } catch (Exception e) {
+ logger.error(CrudServiceMsgs.INVALID_OXM_FILE, file.getName(), e.getMessage());
+ }
+ }
+
+ }
+ }
+ } else {
+ logger.error(CrudServiceMsgs.INVALID_OXM_DIR, CrudServiceConstants.CRD_HOME_MODEL);
+ }
+
+
+ }
+
+ private static void addtimer(String version, File file) {
+ TimerTask task = null;
+ task = new FileWatcher(
+ file) {
+ protected void onChange(File file) {
+ // here we implement the onChange
+ logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+ try {
+ RelationshipSchemaLoader.loadModel(version, file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ if (!timers.containsKey(version)) {
+ Timer timer = new Timer("aai_relationship_" + version);
+ timer.schedule(task, new Date(), 10000);
+ timers.put(version, timer);
+
+ }
+ }
+
+ private synchronized static void loadModel(String version, File file)
+ throws JAXBException, IOException, CrudException {
+
+ InputStream inputStream = new FileInputStream(file);
+ String content = IOUtils.toString(inputStream, "UTF-8");
+ versionContextMap.put(version, new RelationshipSchema(content));
+ addtimer(version, file);
+ versions.add(Integer.parseInt(version.substring(1)));
+ }
+
+ public static RelationshipSchema getSchemaForVersion(String version) throws CrudException {
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ try {
+ loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_relationship_"
+ + version + ".json"));
+ } catch (Exception e) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ public static String getLatestSchemaVersion() throws CrudException {
+ return "v" + versions.last();
+ }
+
+ public static Map<String, RelationshipSchema> getVersionContextMap() {
+ return versionContextMap;
+ }
+
+ public static void setVersionContextMap(HashMap<String, RelationshipSchema> versionContextMap) {
+ RelationshipSchemaLoader.versionContextMap = versionContextMap;
+ }
+
+ public static void main(String[] args) throws FileNotFoundException, Exception {
+ File initialFile = new File("C:\\Software\\gizmo\\src\\main\\java\\org\\openecomp\\schema\\vio.json");
+
+ loadModel("v8", initialFile);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java
new file mode 100644
index 0000000..552b60a
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java
@@ -0,0 +1,341 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.service.EdgePayload;
+import org.openecomp.crud.util.CrudServiceUtil;
+import org.radeox.util.logging.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchemaValidator {
+
+ public static final String SOURCE_NODE = "source";
+ public static final String TARGET_NODE = "target";
+
+ final static Pattern urlPattern = Pattern.compile("services/inventory/(.*)/(.*)/(.*)");
+
+ public static Map<String, Object> resolveCollectionfilter(String version, String type,
+ Map<String, String> filter)
+ throws CrudException {
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+ if (schema == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ HashMap<String, Class<?>> props = schema.lookupRelationType(type);
+ Map<String, Object> result = new HashMap<String, Object>();
+
+ for (String key : filter.keySet()) {
+
+ if (props.containsKey(key)) {
+ try {
+ Object value = CrudServiceUtil.validateFieldType(filter.get(key), props.get(key));
+ result.put(key, value);
+ } catch (Exception ex) {
+ // Skip any exceptions thrown while validating the filter
+ // key value
+ continue;
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ public static void validateType(String version, String type) throws CrudException {
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+ if (!schema.isValidType(type)) {
+ throw new CrudException("Invalid " + RelationshipSchema.SCHEMA_RELATIONSHIP_TYPE
+ + ": " + type,
+ Status.BAD_REQUEST);
+ }
+
+ }
+
+ public static Edge validateIncomingAddPayload(String version, String type, Vertex sourceNode,
+ Vertex targetNode, JsonElement properties)
+ throws CrudException {
+ EdgePayload payload = new EdgePayload();
+ payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+ + "/" + sourceNode.getId().get());
+ payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+ + "/" + targetNode.getId().get());
+ payload.setType(type);
+ payload.setProperties(properties);
+ return validateIncomingAddPayload(version, type, payload);
+ }
+
+ public static Edge validateIncomingAddPayload(String version, String type, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+
+ if (payload.getSource() == null || payload.getTarget() == null) {
+ throw new CrudException("Source/Target not specified", Status.BAD_REQUEST);
+ }
+
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!sourceMatcher.matches() || !targetMatcher.matches()) {
+ throw new CrudException("Invalid Source/Target Urls", Status.BAD_REQUEST);
+ }
+
+ // create key based on source:target:relationshipType
+ String sourceNodeType = sourceMatcher.group(2);
+ String targetNodeType = targetMatcher.group(2);
+
+ String sourceNodeId = sourceMatcher.group(3);
+ String targetNodeId = targetMatcher.group(3);
+
+ String key = sourceNodeType + ":" + targetNodeType + ":" + type;
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ throw new CrudException("Invalid source/target/relationship type: " + key,
+ Status.BAD_REQUEST);
+ }
+
+ Edge.Builder modelEdgeBuilder = new Edge.Builder(type);
+
+ modelEdgeBuilder.source(new Vertex.Builder(sourceNodeType).id(sourceNodeId).build());
+ modelEdgeBuilder.target(new Vertex.Builder(targetNodeType).id(targetNodeId).build());
+
+ // validate it properties
+ validateEdgeProps(modelEdgeBuilder, payload.getProperties(), schemaObject);
+
+ return modelEdgeBuilder.build();
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ public static Edge validateIncomingPatchPayload(Edge edge, String version, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+ if (payload.getSource() != null) {
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+ if (!sourceMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = sourceMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+ throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+ }
+ }
+
+ if (payload.getTarget() != null) {
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!targetMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = targetMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+ throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+ }
+ }
+ // create key based on source:target:relationshipType
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ Logger.warn("key :" + key
+ + " not found in relationship schema . Skipping the schema validation");
+ return edge;
+ }
+
+ Set<Map.Entry<String, JsonElement>> entries = payload.getProperties()
+ .getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+
+ if (!schemaObject.containsKey(entry.getKey())) {
+ throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+ } else if (entry.getValue() instanceof JsonNull && edge.getProperties()
+ .containsKey(entry.getKey())) {
+ edge.getProperties().remove(entry.getKey());
+ } else if (!(entry.getValue() instanceof JsonNull)) {
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ schemaObject.get(entry.getKey()));
+ edge.getProperties().put(entry.getKey(), value);
+ }
+
+ }
+
+ return edge;
+
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+ public static Edge validateIncomingUpdatePayload(Edge edge, String version, Vertex sourceNode,
+ Vertex targetNode, JsonElement properties)
+ throws CrudException {
+ EdgePayload payload = new EdgePayload();
+ payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+ + "/" + sourceNode.getId().get());
+ payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+ + "/" + targetNode.getId().get());
+ payload.setType(edge.getType());
+ payload.setProperties(properties);
+ return validateIncomingUpdatePayload(edge, version, payload);
+ }
+
+ public static Edge validateIncomingUpdatePayload(Edge edge, String version, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+
+ if (payload.getSource() != null) {
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+ if (!sourceMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = sourceMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+ throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+ }
+ }
+
+ if (payload.getTarget() != null) {
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!targetMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = targetMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+ throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+ }
+ }
+ // create key based on source:target:relationshipType
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ Logger.warn("key :" + key
+ + " not found in relationship schema . Skipping the schema validation");
+ return edge;
+ }
+
+ Edge.Builder updatedEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId().get());
+
+ updatedEdgeBuilder
+ .source(new Vertex.Builder(edge.getSource().getType()).id(edge.getSource().getId()
+ .get()).build());
+ updatedEdgeBuilder
+ .target(new Vertex.Builder(edge.getTarget().getType()).id(edge.getTarget().getId()
+ .get()).build());
+
+ validateEdgeProps(updatedEdgeBuilder, payload.getProperties(), schemaObject);
+
+ return updatedEdgeBuilder.build();
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+
+ private static void validateEdgeProps(Edge.Builder builder, JsonElement props,
+ HashMap<String, Class<?>> schemaObject)
+ throws CrudException {
+ Set<Map.Entry<String, JsonElement>> entries = props.getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+
+ if (!schemaObject.containsKey(entry.getKey())) {
+ throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+ } else {
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ schemaObject.get(entry.getKey()));
+ builder.property(entry.getKey(), value);
+ }
+
+ }
+
+ }
+
+ public static Edge validateOutgoingPayload(String version, Edge edge) throws CrudException {
+
+ Edge.Builder modelEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId()
+ .get()).source(edge.getSource())
+ .target(edge.getTarget());
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null || schemaObject.isEmpty()) {
+ return edge;
+ }
+
+ for (String prop : edge.getProperties().keySet()) {
+ if (schemaObject.containsKey(prop)) {
+ modelEdgeBuilder.property(prop, edge.getProperties().get(prop));
+ }
+
+ }
+ return modelEdgeBuilder.build();
+ }
+
+}