aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/onap/aai/spike/schema
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/onap/aai/spike/schema')
-rw-r--r--src/main/java/org/onap/aai/spike/schema/EdgeRulesLoader.java229
-rw-r--r--src/main/java/org/onap/aai/spike/schema/GraphEventTransformer.java282
-rw-r--r--src/main/java/org/onap/aai/spike/schema/MapAdapter.java65
-rw-r--r--src/main/java/org/onap/aai/spike/schema/OXMModelLoader.java187
-rw-r--r--src/main/java/org/onap/aai/spike/schema/OxmConfigTranslator.java98
-rw-r--r--src/main/java/org/onap/aai/spike/schema/Relationship.java102
-rw-r--r--src/main/java/org/onap/aai/spike/schema/RelationshipSchema.java133
7 files changed, 1096 insertions, 0 deletions
diff --git a/src/main/java/org/onap/aai/spike/schema/EdgeRulesLoader.java b/src/main/java/org/onap/aai/spike/schema/EdgeRulesLoader.java
new file mode 100644
index 0000000..b914421
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/EdgeRulesLoader.java
@@ -0,0 +1,229 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import com.google.common.collect.Multimap;
+import org.apache.commons.io.IOUtils;
+import org.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.edges.EdgeIngestor;
+import org.onap.aai.edges.EdgeRule;
+import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
+import org.onap.aai.setup.ConfigTranslator;
+import org.onap.aai.setup.SchemaLocationsBean;
+import org.onap.aai.setup.Version;
+import org.onap.aai.spike.exception.SpikeException;
+import org.onap.aai.spike.logging.SpikeMsgs;
+import org.onap.aai.spike.util.SchemaIngestPropertiesReader;
+
+
+public class EdgeRulesLoader {
+
+ private static Map<String, RelationshipSchema> versionContextMap = new ConcurrentHashMap<>();
+
+ static final Pattern versionPattern = Pattern.compile("V(\\d*)");
+ static final String propsPrefix = "edge_properties_";
+ static final String propsSuffix = ".json";
+ final static Pattern propsFilePattern = Pattern.compile(propsPrefix + "(.*)" + propsSuffix);
+ final static Pattern propsVersionPattern = Pattern.compile("v\\d*");
+
+ private static org.onap.aai.cl.api.Logger logger =
+ LoggerFactory.getInstance().getLogger(EdgeRulesLoader.class.getName());
+
+ private EdgeRulesLoader() {}
+
+ /**
+ * Finds all DB Edge Rules and Edge Properties files for all OXM models.
+ *
+ * @throws SpikeException
+ */
+ public static synchronized void loadModels() throws SpikeException {
+ SchemaIngestPropertiesReader SchemaIngestPropertiesReader = new SchemaIngestPropertiesReader();
+ SchemaLocationsBean schemaLocationsBean = new SchemaLocationsBean();
+ schemaLocationsBean.setEdgeDirectory(SchemaIngestPropertiesReader.getEdgeDir());
+ ConfigTranslator configTranslator = new OxmConfigTranslator(schemaLocationsBean);
+ EdgeIngestor edgeIngestor = new EdgeIngestor(configTranslator);
+ Map<String, File> propFiles = edgePropertyFiles(SchemaIngestPropertiesReader);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading DB Edge Rules");
+ }
+
+ for (String version : OXMModelLoader.getLoadedOXMVersions()) {
+ try {
+ loadModel(Version.valueOf(version), edgeIngestor, propFiles);
+ } catch (IOException | EdgeRuleNotFoundException e) {
+ throw new SpikeException(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Loads DB Edge Rules and Edge Properties for a given version.
+ *
+ * @throws SpikeException
+ */
+
+ public static synchronized void loadModels(String v) throws SpikeException {
+ SchemaIngestPropertiesReader SchemaIngestPropertiesReader = new SchemaIngestPropertiesReader();
+ SchemaLocationsBean schemaLocationsBean = new SchemaLocationsBean();
+ schemaLocationsBean.setEdgeDirectory(SchemaIngestPropertiesReader.getEdgeDir());
+ ConfigTranslator configTranslator = new OxmConfigTranslator(schemaLocationsBean);
+ EdgeIngestor edgeIngestor = new EdgeIngestor(configTranslator);
+ String version = v.toUpperCase();
+ Map<String, File> propFiles = edgePropertyFiles(SchemaIngestPropertiesReader);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading DB Edge Rules ");
+ }
+
+ try {
+ loadModel(Version.valueOf(version), edgeIngestor, propFiles);
+ } catch (IOException | EdgeRuleNotFoundException e) {
+ throw new SpikeException(e.getMessage());
+ }
+ }
+
+ /**
+ * Retrieves the DB Edge Rule relationship schema for a given version.
+ *
+ * @param version - The OXM version that we want the DB Edge Rule for.
+ * @return - A RelationshipSchema of the DB Edge Rule for the OXM version.
+ * @throws SpikeException
+ */
+ public static RelationshipSchema getSchemaForVersion(String version) throws SpikeException {
+
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ logger.error(SpikeMsgs.OXM_LOAD_ERROR, "Error loading DB Edge Rules for: " + version);
+ throw new SpikeException("Error loading DB Edge Rules for: " + version);
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ /**
+ * Retrieves the DB Edge Rule relationship schema for all loaded OXM versions.
+ *
+ * @return - A Map of the OXM version and it's corresponding RelationshipSchema of the DB Edge Rule.
+ * @throws SpikeException
+ */
+ public static Map<String, RelationshipSchema> getSchemas() throws SpikeException {
+
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ }
+ return versionContextMap;
+ }
+
+ /**
+ * Returns the latest available DB Edge Rule version.
+ *
+ * @return - A Map of the OXM version and it's corresponding RelationshipSchema of the DB Edge Rule.
+ * @throws SpikeException
+ */
+ public static String getLatestSchemaVersion() throws SpikeException {
+
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ }
+
+ // If there are still no models available, then there's not much we can do...
+ if (versionContextMap.isEmpty()) {
+ logger.error(SpikeMsgs.OXM_LOAD_ERROR, "No available DB Edge Rules to get latest version for.");
+ throw new SpikeException("No available DB Edge Rules to get latest version for.");
+ }
+
+ // Iterate over the available model versions to determine which is the most
+ // recent.
+ Integer latestVersion = null;
+ String latestVersionStr = null;
+ for (String versionKey : versionContextMap.keySet()) {
+
+ Matcher matcher = versionPattern.matcher(versionKey.toUpperCase());
+ if (matcher.find()) {
+
+ int currentVersion = Integer.parseInt(matcher.group(1));
+
+ if ((latestVersion == null) || (currentVersion > latestVersion)) {
+ latestVersion = currentVersion;
+ latestVersionStr = versionKey;
+ }
+ }
+ }
+
+ return latestVersionStr;
+ }
+
+ /**
+ * Reset the loaded DB Edge Rule schemas
+ *
+ */
+
+ public static void resetSchemaVersionContext() {
+ versionContextMap = new ConcurrentHashMap<>();
+ }
+
+ private static synchronized void loadModel(Version version, EdgeIngestor edgeIngestor, Map<String, File> props)
+ throws IOException, SpikeException, EdgeRuleNotFoundException {
+
+ Multimap<String, EdgeRule> edges = edgeIngestor.getAllRules(version);
+ String edgeProps;
+ if (props.get(version.toString().toLowerCase()) != null) {
+ edgeProps = IOUtils.toString(new FileInputStream(props.get(version.toString().toLowerCase())), "UTF-8");
+ } else {
+ throw new FileNotFoundException("The Edge Properties file for OXM version " + version + "was not found.");
+ }
+ if (edges != null) {
+ RelationshipSchema rs = new RelationshipSchema(edges, edgeProps);
+ versionContextMap.put(version.toString().toLowerCase(), rs);
+ logger.info(SpikeMsgs.LOADED_DB_RULE_FILE, version.toString());
+ }
+ }
+
+ private static Map<String, File> edgePropertyFiles(SchemaIngestPropertiesReader dir) throws SpikeException {
+ Map<String, File> propsFiles = Arrays
+ .stream(new File(dir.getEdgePropsDir())
+ .listFiles((d, name) -> propsFilePattern.matcher(name).matches()))
+ .collect(Collectors.toMap(new Function<File, String>() {
+ public String apply(File f) {
+ Matcher m1 = propsVersionPattern.matcher(f.getName());
+ m1.find();
+ return m1.group(0);
+ }
+ }, f -> f));
+ return propsFiles;
+ }
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/GraphEventTransformer.java b/src/main/java/org/onap/aai/spike/schema/GraphEventTransformer.java
new file mode 100644
index 0000000..ced84bb
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/GraphEventTransformer.java
@@ -0,0 +1,282 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+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.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.spike.event.incoming.GizmoEdge;
+import org.onap.aai.spike.event.incoming.GizmoGraphEvent;
+import org.onap.aai.spike.event.incoming.GizmoVertex;
+import org.onap.aai.spike.exception.SpikeException;
+
+/**
+ * This class is responsible for transforming raw graph entities (such as vertices and edges) into
+ * representations which correspond to the OXM models.
+ */
+public class GraphEventTransformer {
+
+ private static org.onap.aai.cl.api.Logger logger =
+ LoggerFactory.getInstance().getLogger(GraphEventTransformer.class.getName());
+ private static final String AAI_UUID = "aai-uuid";
+
+ /**
+ *
+ * @param rawVertex
+ * @throws SpikeException
+ */
+ public static void validateVertexModel(GizmoVertex rawVertex) throws SpikeException {
+
+ validateVertexModel(OXMModelLoader.getLatestVersion(), rawVertex);
+ }
+
+ public static void populateUUID(GizmoGraphEvent event) throws SpikeException {
+ try {
+ if (event.getVertex() != null) {
+ if (event.getVertex().getProperties().getAsJsonObject().has(AAI_UUID)) {
+ event.getVertex()
+ .setId(event.getVertex().getProperties().getAsJsonObject().get(AAI_UUID).getAsString());
+ }
+ } else if (event.getRelationship() != null) {
+ if (event.getRelationship().getProperties().getAsJsonObject().has(AAI_UUID)) {
+ event.getRelationship().setId(
+ event.getRelationship().getProperties().getAsJsonObject().get(AAI_UUID).getAsString());
+ }
+
+ if (event.getRelationship().getSource().getProperties().getAsJsonObject().has(AAI_UUID)) {
+ event.getRelationship().getSource().setId(event.getRelationship().getSource().getProperties()
+ .getAsJsonObject().get(AAI_UUID).getAsString());
+ }
+ if (event.getRelationship().getTarget().getProperties().getAsJsonObject().has(AAI_UUID)) {
+ event.getRelationship().getTarget().setId(event.getRelationship().getTarget().getProperties()
+ .getAsJsonObject().get(AAI_UUID).getAsString());
+ }
+ }
+ } catch (Exception ex) {
+ throw new SpikeException("Unable to parse uuid in incoming event");
+ }
+ }
+
+ /**
+ *
+ * @param version
+ * @param rawVertex
+ * @throws SpikeException
+ */
+ public static void validateVertexModel(String version, GizmoVertex rawVertex) throws SpikeException {
+
+ try {
+
+ DynamicJAXBContext jaxbContext = OXMModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, rawVertex.getType()));
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+ final DynamicType reservedType = jaxbContext.getDynamicType("ReservedPropNames");
+
+ Set<Map.Entry<String, JsonElement>> vertexEntriesSet =
+ rawVertex.getProperties().getAsJsonObject().entrySet();
+ Map<String, JsonElement> vertexEntriesMap = new HashMap<String, JsonElement>();
+ for (Map.Entry<String, JsonElement> entry : vertexEntriesSet) {
+ vertexEntriesMap.put(entry.getKey(), entry.getValue());
+ }
+
+ JsonObject modelJsonElement = new JsonObject();
+ // Iterate over all of the attributes specified in the model schema,
+ // populating
+ // our dynamic instance with the corresponding values supplied in
+ // our raw vertex.
+ for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
+ if (mapping.isAbstractDirectMapping()) {
+ DatabaseField f = mapping.getField();
+ String keyName = f.getName().substring(0, f.getName().indexOf("/"));
+
+ String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
+ : mapping.getProperties().get("defaultValue").toString();
+
+ if (((XMLField) f).isRequired() && !vertexEntriesMap.containsKey(keyName)
+ && !defaultValue.isEmpty()) {
+ modelJsonElement.addProperty(keyName, defaultValue);
+
+ }
+ // If this is a required field, but is not present in the
+ // raw vertex, reject this
+ // as an invalid input since we can't build a valid object
+ // from what we were provided.
+ if (((XMLField) f).isRequired() && !vertexEntriesMap.containsKey(keyName)
+ && defaultValue.isEmpty()) {
+ throw new SpikeException("Missing required field: " + keyName);
+ }
+
+ // If this is a non-required field, then set it if a value
+ // was provided in the
+ // raw vertex.
+ if (vertexEntriesMap.containsKey(keyName)) {
+ validateFieldType(vertexEntriesMap.get(keyName), f.getType());
+ modelJsonElement.add(keyName, vertexEntriesMap.get(keyName));
+ }
+ }
+ }
+
+ // Ensure any of the reserved properties are added to the payload
+ for (DatabaseMapping mapping : reservedType.getDescriptor().getMappings()) {
+ if (mapping.isAbstractDirectMapping()) {
+ DatabaseField field = mapping.getField();
+ String keyName = field.getName().substring(0, field.getName().indexOf("/"));
+
+ if (vertexEntriesMap.containsKey(keyName)) {
+ validateFieldType(vertexEntriesMap.get(keyName), field.getType());
+ modelJsonElement.add(keyName, vertexEntriesMap.get(keyName));
+ }
+ }
+ }
+
+ rawVertex.setProperties(modelJsonElement);
+ } catch (Exception e) {
+ throw new SpikeException(e.getMessage());
+ }
+ }
+
+ /**
+ *
+ * @param rawEdge
+ * @throws SpikeException
+ */
+ public static void validateEdgeModel(GizmoEdge rawEdge) throws SpikeException {
+
+ validateEdgeModel(EdgeRulesLoader.getLatestSchemaVersion(), rawEdge);
+ }
+
+ /**
+ *
+ * @param version
+ * @param rawEdge
+ * @throws SpikeException
+ */
+ public static void validateEdgeModel(String version, GizmoEdge rawEdge) throws SpikeException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Convert edge: " + rawEdge.toString() + " to model version: " + version);
+ }
+
+ // Get the relationship schema for the supplied version.
+ RelationshipSchema schema = EdgeRulesLoader.getSchemaForVersion(version);
+
+ try {
+
+ // Validate that our edge does have the necessary endpoints.
+ if (rawEdge.getSource() == null || rawEdge.getTarget() == null) {
+ throw new SpikeException("Source or target endpoint not specified");
+ }
+
+ // Create a key based on source:target:relationshipType
+ String sourceNodeType = rawEdge.getSource().getType();
+ String targetNodeType = rawEdge.getTarget().getType();
+ String key = sourceNodeType + ":" + targetNodeType + ":" + rawEdge.getType();
+
+ // Now, look up the specific schema model based on the key we just
+ // constructed.
+ Map<String, Class<?>> relationshipModel = schema.lookupRelation(key);
+ if (relationshipModel == null || relationshipModel.isEmpty()) {
+ throw new SpikeException("Invalid source/target/relationship type: " + key);
+ }
+
+ Set<Map.Entry<String, JsonElement>> edgeEntriesSet = rawEdge.getProperties().getAsJsonObject().entrySet();
+ Map<String, JsonElement> edgeEntriesMap = new HashMap<String, JsonElement>();
+ for (Map.Entry<String, JsonElement> entry : edgeEntriesSet) {
+ edgeEntriesMap.put(entry.getKey(), entry.getValue());
+ }
+
+ JsonObject modelJsonElement = new JsonObject();
+
+ for (String property : relationshipModel.keySet()) {
+
+ if (!edgeEntriesMap.containsKey(property)) {
+ throw new SpikeException("Missing required field: " + property);
+ }
+
+ validateFieldType(edgeEntriesMap.get(property), relationshipModel.get(property));
+ modelJsonElement.add(property, edgeEntriesMap.get(property));
+
+ }
+
+ rawEdge.setProperties(modelJsonElement);
+
+
+ } catch (Exception ex) {
+ throw new SpikeException(ex.getMessage());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Object validateFieldType(JsonElement value, Class clazz) throws SpikeException {
+ try {
+ if (clazz.isAssignableFrom(Integer.class)) {
+ return value.getAsInt();
+ } else if (clazz.isAssignableFrom(Long.class)) {
+ return value.getAsLong();
+ } else if (clazz.isAssignableFrom(Float.class)) {
+ return value.getAsFloat();
+ } else if (clazz.isAssignableFrom(Double.class)) {
+ return value.getAsDouble();
+ } else if (clazz.isAssignableFrom(Boolean.class)) {
+ return value.getAsBoolean();
+ } else {
+ return value;
+ }
+
+ } catch (Exception e) {
+ throw new SpikeException("Invalid property value: " + value);
+ }
+ }
+
+ public static Object validateFieldType(String value, Class clazz) throws SpikeException {
+ try {
+ if (clazz.isAssignableFrom(Integer.class)) {
+ return Integer.parseInt(value);
+ } else if (clazz.isAssignableFrom(Long.class)) {
+ return Long.parseLong(value);
+ } else if (clazz.isAssignableFrom(Float.class)) {
+ return Float.parseFloat(value);
+ } else if (clazz.isAssignableFrom(Double.class)) {
+ return Double.parseDouble(value);
+ } else if (clazz.isAssignableFrom(Boolean.class)) {
+ if (!value.equals("true") && !value.equals("false")) {
+ throw new SpikeException("Invalid property value: " + value);
+ }
+ return Boolean.parseBoolean(value);
+ } else {
+ return value;
+ }
+ } catch (Exception e) {
+ throw new SpikeException("Invalid property value: " + value);
+ }
+ }
+
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/MapAdapter.java b/src/main/java/org/onap/aai/spike/schema/MapAdapter.java
new file mode 100644
index 0000000..2e59450
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/MapAdapter.java
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.naming.OperationNotSupportedException;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlAnyElement;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.namespace.QName;
+
+
+public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, Object>> {
+
+ public static class AdaptedMap {
+ @XmlAnyElement
+ List elements;
+ }
+
+ @Override
+ public Map<String, Object> unmarshal(AdaptedMap map) throws Exception {
+ throw new OperationNotSupportedException(); // really??
+ }
+
+ @Override
+ public AdaptedMap marshal(Map<String, Object> map) throws Exception {
+
+ AdaptedMap adaptedMap = new AdaptedMap();
+ List elements = new ArrayList();
+ for (Map.Entry<String, Object> property : map.entrySet()) {
+
+ if (property.getValue() instanceof Map) {
+ elements.add(new JAXBElement<AdaptedMap>(new QName(property.getKey()), MapAdapter.AdaptedMap.class,
+ marshal((Map) property.getValue())));
+
+ } else {
+
+ elements.add(new JAXBElement<String>(new QName(property.getKey()), String.class,
+ property.getValue().toString()));
+ }
+ }
+ adaptedMap.elements = elements;
+ return adaptedMap;
+ }
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/OXMModelLoader.java b/src/main/java/org/onap/aai/spike/schema/OXMModelLoader.java
new file mode 100644
index 0000000..0d174db
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/OXMModelLoader.java
@@ -0,0 +1,187 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.nodes.NodeIngestor;
+import org.onap.aai.setup.ConfigTranslator;
+import org.onap.aai.setup.SchemaLocationsBean;
+import org.onap.aai.setup.Version;
+import org.onap.aai.spike.exception.SpikeException;
+import org.onap.aai.spike.logging.SpikeMsgs;
+import org.onap.aai.spike.util.SchemaIngestPropertiesReader;
+
+/**
+ * This class contains all of the logic for importing OXM model schemas from the available OXM
+ * schema files.
+ */
+public class OXMModelLoader {
+
+ private static Map<String, DynamicJAXBContext> versionContextMap =
+ new ConcurrentHashMap<String, DynamicJAXBContext>();
+
+ final static Pattern p = Pattern.compile("aai_oxm_(.*).xml");
+ final static Pattern versionPattern = Pattern.compile("V(\\d*)");
+
+ private static org.onap.aai.cl.api.Logger logger =
+ LoggerFactory.getInstance().getLogger(OXMModelLoader.class.getName());
+
+ /**
+ * Finds all OXM model files
+ *
+ * @throws SpikeException
+ * @throws IOException
+ *
+ */
+ public synchronized static void loadModels() throws SpikeException {
+ SchemaIngestPropertiesReader schemaIngestPropReader = new SchemaIngestPropertiesReader();
+ SchemaLocationsBean schemaLocationsBean = new SchemaLocationsBean();
+ schemaLocationsBean.setNodeDirectory(schemaIngestPropReader.getNodeDir());
+ schemaLocationsBean.setEdgeDirectory(schemaIngestPropReader.getEdgeDir());
+ ConfigTranslator configTranslator = new OxmConfigTranslator(schemaLocationsBean);
+ NodeIngestor nodeIngestor = new NodeIngestor(configTranslator);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading OXM Models");
+ }
+
+ for (Version oxmVersion : Version.values()) {
+ DynamicJAXBContext jaxbContext = nodeIngestor.getContextForVersion(oxmVersion);
+ if (jaxbContext != null) {
+ loadModel(oxmVersion.toString(), jaxbContext);
+ }
+ }
+ }
+
+
+ private synchronized static void loadModel(String oxmVersion, DynamicJAXBContext jaxbContext) {
+ versionContextMap.put(oxmVersion, jaxbContext);
+ logger.info(SpikeMsgs.LOADED_OXM_FILE, oxmVersion);
+ }
+
+ /**
+ * Retrieves the JAXB context for the specified OXM model version.
+ *
+ * @param version - The OXM version that we want the JAXB context for.
+ *
+ * @return - A JAXB context derived from the OXM model schema.
+ *
+ * @throws SpikeException
+ */
+ public static DynamicJAXBContext getContextForVersion(String version) throws SpikeException {
+
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ throw new SpikeException("Error loading oxm model: " + version);
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ public static String getLatestVersion() throws SpikeException {
+
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ }
+
+ // If there are still no models available, then there's not much we can do...
+ if (versionContextMap.isEmpty()) {
+ throw new SpikeException("No available OXM schemas to get latest version for.");
+ }
+
+ // Iterate over the available model versions to determine which is the most
+ // recent.
+ Integer latestVersion = null;
+ String latestVersionStr = null;
+ for (String versionKey : versionContextMap.keySet()) {
+
+ Matcher matcher = versionPattern.matcher(versionKey);
+ if (matcher.find()) {
+
+ int currentVersion = Integer.valueOf(matcher.group(1));
+
+ if ((latestVersion == null) || (currentVersion > latestVersion)) {
+ latestVersion = currentVersion;
+ latestVersionStr = versionKey;
+ }
+ }
+ }
+
+ return latestVersionStr;
+ }
+
+ /**
+ * Retrieves the map of all JAXB context objects that have been created by importing the available
+ * OXM model schemas.
+ *
+ * @return - Map of JAXB context objects.
+ */
+ public static Map<String, DynamicJAXBContext> getVersionContextMap() {
+ return versionContextMap;
+ }
+
+ /**
+ * Assigns the map of all JAXB context objects.
+ *
+ * @param versionContextMap
+ */
+ public static void setVersionContextMap(Map<String, DynamicJAXBContext> versionContextMap) {
+ OXMModelLoader.versionContextMap = versionContextMap;
+ }
+
+ /**
+ * Retrieves the list of all Loaded OXM versions.
+ *
+ * @return - A List of Strings of all loaded OXM versions.
+ *
+ * @throws SpikeException
+ */
+ public static List<String> getLoadedOXMVersions() throws SpikeException {
+ // If we haven't already loaded in the available OXM models, then do so now.
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ }
+ // If there are still no models available, then there's not much we can do...
+ if (versionContextMap.isEmpty()) {
+ logger.error(SpikeMsgs.OXM_LOAD_ERROR, "No available OXM schemas to get versions for.");
+ throw new SpikeException("No available OXM schemas to get latest version for.");
+ }
+ List<String> versions = new ArrayList<String>();
+ for (String versionKey : versionContextMap.keySet()) {
+ Matcher matcher = versionPattern.matcher(versionKey.toUpperCase());
+ if (matcher.find()) {
+ versions.add("V" + matcher.group(1));
+ }
+ }
+ return versions;
+ }
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/OxmConfigTranslator.java b/src/main/java/org/onap/aai/spike/schema/OxmConfigTranslator.java
new file mode 100644
index 0000000..51ed93e
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/OxmConfigTranslator.java
@@ -0,0 +1,98 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.onap.aai.setup.ConfigTranslator;
+import org.onap.aai.setup.SchemaLocationsBean;
+import org.onap.aai.setup.Version;
+
+public class OxmConfigTranslator extends ConfigTranslator {
+ public OxmConfigTranslator(SchemaLocationsBean bean) {
+ super(bean);
+ }
+
+ @Override
+ public Map<Version, List<String>> getNodeFiles() {
+ String nodeDirectory = bean.getNodeDirectory();
+ if (nodeDirectory == null) {
+ throw new ServiceConfigurationError(
+ "Node(s) directory is empty in the schema location bean (" + bean.getSchemaConfigLocation() + ")");
+ }
+ try {
+ return getVersionMap(Paths.get(nodeDirectory), "*_v*.xml");
+ } catch (IOException e) {
+ throw new ServiceConfigurationError("Failed to read node(s) directory " + getPath(nodeDirectory), e);
+ }
+ }
+
+ @Override
+ public Map<Version, List<String>> getEdgeFiles() {
+ String edgeDirectory = bean.getEdgeDirectory();
+ if (edgeDirectory == null) {
+ throw new ServiceConfigurationError(
+ "Edge(s) directory is empty in the schema location bean (" + bean.getSchemaConfigLocation() + ")");
+ }
+ try {
+ return getVersionMap(Paths.get(edgeDirectory), "*_v*.json");
+ } catch (IOException e) {
+ throw new ServiceConfigurationError("Failed to read edge(s) directory " + getPath(edgeDirectory), e);
+ }
+ }
+
+ private String getPath(String nodeDirectory) {
+ return Paths.get(nodeDirectory).toAbsolutePath().toString();
+ }
+
+ /**
+ * Creates a map containing each OXM Version and the matching OXM file path(s)
+ *
+ * @param folderPath the folder/directory containing the OXM files
+ * @param fileSuffix
+ * @return a new Map object (may be empty)
+ * @throws IOException if there is a problem reading the specified directory path
+ */
+ private Map<Version, List<String>> getVersionMap(Path folderPath, String globPattern) throws IOException {
+ final PathMatcher filter = folderPath.getFileSystem().getPathMatcher("glob:**/" + globPattern);
+ try (final Stream<Path> stream = Files.list(folderPath)) {
+ return stream.filter(filter::matches).map(Path::toString).filter(p -> getVersionFromPath(p) != null)
+ .collect(Collectors.groupingBy(this::getVersionFromPath));
+ }
+ }
+
+ private Version getVersionFromPath(String pathName) {
+ String version = "V" + pathName.replaceAll("^.*\\/", "").replaceAll("\\D+", "");
+ try {
+ return Version.valueOf(version);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/Relationship.java b/src/main/java/org/onap/aai/spike/schema/Relationship.java
new file mode 100644
index 0000000..af81a29
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/Relationship.java
@@ -0,0 +1,102 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.spike.schema;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import com.google.gson.annotations.SerializedName;
+
+
+/**
+ * This class represents a relationship instance that can be used for marshalling and unmarshalling
+ * to/from a model compliant representation.
+ */
+@XmlRootElement
+public class Relationship {
+
+ /** Unique identifier for this relationship. */
+ private String id;
+
+ /** Relationship type. */
+ private String type;
+
+ /** The source vertex for this edge. */
+ @SerializedName("source-node-type")
+ private String sourceNodeType;
+
+ /** The target vertex for this edge. */
+ @SerializedName("target-node-type")
+ private String targetNodeType;
+
+ /** The properties assigned to this edge. */
+ private Map<String, Object> properties = new HashMap<String, Object>();
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getSourceNodeType() {
+ return sourceNodeType;
+ }
+
+ @XmlElement(name = "source-node-type")
+ public void setSourceNodeType(String sourceNodeType) {
+ this.sourceNodeType = sourceNodeType;
+ }
+
+ public String getTargetNodeType() {
+ return targetNodeType;
+ }
+
+ @XmlElement(name = "target-node-type")
+ public void setTargetNodeType(String targetNodeType) {
+ this.targetNodeType = targetNodeType;
+ }
+
+ @XmlJavaTypeAdapter(MapAdapter.class)
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+ public void setProperty(String key, Object property) {
+ properties.put(key, property);
+ }
+}
diff --git a/src/main/java/org/onap/aai/spike/schema/RelationshipSchema.java b/src/main/java/org/onap/aai/spike/schema/RelationshipSchema.java
new file mode 100644
index 0000000..6858783
--- /dev/null
+++ b/src/main/java/org/onap/aai/spike/schema/RelationshipSchema.java
@@ -0,0 +1,133 @@
+/**
+ * ============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.onap.aai.spike.schema;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import com.google.common.collect.Multimap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.onap.aai.edges.EdgeRule;
+import org.onap.aai.spike.exception.SpikeException;
+
+
+public class RelationshipSchema {
+ private static final Gson gson = new GsonBuilder().create();
+
+ public static final String SCHEMA_SOURCE_NODE_TYPE = "from";
+ public static final String SCHEMA_TARGET_NODE_TYPE = "to";
+ public static final String SCHEMA_RELATIONSHIP_TYPE = "label";
+ public static final String SCHEMA_RULES_ARRAY = "rules";
+
+
+ private Map<String, Map<String, Class<?>>> relations = new HashMap<>();
+ /**
+ * Hashmap of valid relationship types along with properties.
+ */
+ private Map<String, Map<String, Class<?>>> relationTypes = new HashMap<>();
+
+
+ public RelationshipSchema(Multimap<String, EdgeRule> rules, String props) throws SpikeException, IOException {
+ HashMap<String, String> properties = new ObjectMapper().readValue(props, HashMap.class);
+ Map<String, Class<?>> edgeProps =
+ properties.entrySet().stream().collect(Collectors.toMap(p -> p.getKey(), p -> {
+ try {
+ return resolveClass(p.getValue());
+ } catch (SpikeException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }));
+
+ rules.entries().forEach((kv) -> {
+ relationTypes.put(kv.getValue().getLabel(), edgeProps);
+ relations.put(buildRelation(kv.getValue().getFrom(), kv.getValue().getTo(), kv.getValue().getLabel()),
+ edgeProps);
+ });
+ }
+
+ public RelationshipSchema(List<String> jsonStrings) throws SpikeException, IOException {
+ String edgeRules = jsonStrings.get(0);
+ String props = jsonStrings.get(1);
+
+ HashMap<String, ArrayList<LinkedHashMap<String, String>>> rules =
+ new ObjectMapper().readValue(edgeRules, HashMap.class);
+ HashMap<String, String> properties = new ObjectMapper().readValue(props, HashMap.class);
+ Map<String, Class<?>> edgeProps =
+ properties.entrySet().stream().collect(Collectors.toMap(p -> p.getKey(), p -> {
+ try {
+ return resolveClass(p.getValue());
+ } catch (SpikeException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }));
+
+ rules.get(SCHEMA_RULES_ARRAY).forEach(l -> {
+ relationTypes.put(l.get(SCHEMA_RELATIONSHIP_TYPE), edgeProps);
+ relations.put(buildRelation(l.get(SCHEMA_SOURCE_NODE_TYPE), l.get(SCHEMA_TARGET_NODE_TYPE),
+ l.get(SCHEMA_RELATIONSHIP_TYPE)), edgeProps);
+ });
+ }
+
+
+
+ public Map<String, Class<?>> lookupRelation(String key) {
+ return this.relations.get(key);
+ }
+
+ public Map<String, Class<?>> lookupRelationType(String type) {
+ return this.relationTypes.get(type);
+ }
+
+ public boolean isValidType(String type) {
+ return relationTypes.containsKey(type);
+ }
+
+
+ private String buildRelation(String source, String target, String relation) {
+ return source + ":" + target + ":" + relation;
+ }
+
+ private Class<?> resolveClass(String type) throws SpikeException, ClassNotFoundException {
+ Class<?> clazz = Class.forName(type);
+ validateClassTypes(clazz);
+ return clazz;
+ }
+
+ private void validateClassTypes(Class<?> clazz) throws SpikeException {
+ if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class)
+ && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) {
+ throw new SpikeException("BAD_REQUEST");
+ }
+ }
+}
+
+