diff options
Diffstat (limited to 'src/main/java/org/onap/aai/spike/schema')
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"); + } + } +} + + |