2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017 Amdocs
7 * ===================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END============================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.openecomp.aai.champ.ie;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.LinkedList;
31 import java.util.List;
33 import java.util.Map.Entry;
34 import java.util.Optional;
36 import java.util.concurrent.atomic.AtomicInteger;
38 import javax.xml.parsers.DocumentBuilder;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.parsers.ParserConfigurationException;
41 import javax.xml.stream.XMLOutputFactory;
42 import javax.xml.stream.XMLStreamException;
43 import javax.xml.stream.XMLStreamWriter;
45 import org.openecomp.aai.champ.ChampAPI;
46 import org.openecomp.aai.champ.ChampGraph;
47 import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
48 import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
49 import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
50 import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
51 import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
52 import org.openecomp.aai.champ.model.ChampObject;
53 import org.openecomp.aai.champ.model.ChampObjectIndex;
54 import org.openecomp.aai.champ.model.ChampRelationship;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.InputSource;
62 import org.xml.sax.SAXException;
64 public class GraphMLImporterExporter implements Importer, Exporter {
66 private static final Logger LOGGER = LoggerFactory.getLogger(GraphMLImporterExporter.class);
68 private static class GraphMLKey {
69 private final String id;
70 private final String attrName;
71 private final String attrType;
73 public GraphMLKey(String id, String attrName, Class<?> attrType) {
75 this.attrName = attrName;
77 if (attrType.equals(Boolean.class)) {
78 this.attrType = "boolean";
79 } else if (attrType.equals(Integer.class)) {
80 this.attrType = "int";
81 } else if (attrType.equals(Long.class)) {
82 this.attrType = "long";
83 } else if (attrType.equals(Float.class)) {
84 this.attrType = "float";
85 } else if (attrType.equals(Double.class)) {
86 this.attrType = "double";
87 } else if (attrType.equals(String.class)) {
88 this.attrType = "string";
90 throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
95 public void importData(ChampAPI api, InputStream is) {
98 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
99 final DocumentBuilder builder = factory.newDocumentBuilder();
100 final InputSource inputSource = new InputSource(is);
101 final Document doc = builder.parse(inputSource);
103 final Map<String, Map<String, String>> nodePropertyDefinitions = new HashMap<String, Map<String, String>> ();
104 final Map<String, Map<String, String>> edgePropertyDefinitions = new HashMap<String, Map<String, String>> ();
105 final Set<Map<String, String>> nodeDefaults = new HashSet<Map<String, String>> ();
106 final Set<Map<String, String>> edgeDefaults = new HashSet<Map<String, String>> ();
108 final NodeList keys = doc.getElementsByTagName("key");
110 for (int i = 0; i < keys.getLength(); i++) {
111 final Node key = keys.item(i);
112 final String id = key.getAttributes().getNamedItem("id").getNodeValue();
113 final String attrName = key.getAttributes().getNamedItem("attr.name").getNodeValue();
114 final String attrType = key.getAttributes().getNamedItem("attr.type").getNodeValue();
115 final String elementType = key.getAttributes().getNamedItem("for").getNodeValue();
116 final Map<String, String> propertyDefinitions = new HashMap<String, String> ();
118 propertyDefinitions.put("attr.name", attrName);
119 propertyDefinitions.put("attr.type", attrType);
121 final NodeList keyChildren = key.getChildNodes();
123 for (int j = 0; j < keyChildren.getLength(); j++) {
124 final Node keyChild = keyChildren.item(j);
126 if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
128 if (keyChild.getNodeName().equals("default")) {
129 propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
131 if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
132 else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
136 if (elementType.equals("node")) {
137 nodePropertyDefinitions.put(id, propertyDefinitions);
138 } else if (elementType.equals("edge")) {
139 edgePropertyDefinitions.put(id, propertyDefinitions);
141 LOGGER.warn("Unknown element type {}, skipping", elementType);
145 final NodeList graphs = doc.getElementsByTagName("graph");
147 for (int i = 0; i < graphs.getLength(); i++) {
148 final Node graph = graphs.item(i);
149 final String graphName = graph.getAttributes().getNamedItem("id").getNodeValue();
150 final NodeList nodesAndEdges = graph.getChildNodes();
152 api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
153 .ofName("importAssignedId")
155 .forField("importAssignedId")
158 for (int j = 0; j < nodesAndEdges.getLength(); j++) {
159 final Node nodeOrEdge = nodesAndEdges.item(j);
161 if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
163 if (nodeOrEdge.getNodeName().equals("node")) {
164 writeNode(api.getGraph(graphName), nodeOrEdge, nodePropertyDefinitions, nodeDefaults);
165 } else if (nodeOrEdge.getNodeName().equals("edge")) {
166 writeEdge(api.getGraph(graphName), nodeOrEdge, edgePropertyDefinitions, edgeDefaults);
168 LOGGER.warn("Unknown object {} found in graphML, skipping", nodeOrEdge.getNodeName());
172 } catch (ParserConfigurationException e) {
173 throw new RuntimeException("Failed to setup DocumentBuilder", e);
174 } catch (SAXException e) {
175 throw new RuntimeException("Failed to parse input stream", e);
176 } catch (IOException e) {
177 throw new RuntimeException("Failed to parse input stream", e);
181 private void writeEdge(ChampGraph graph, Node edge, Map<String, Map<String, String>> edgePropertyDefinitions, Set<Map<String, String>> edgeDefaults) {
182 final NamedNodeMap edgeAttributes = edge.getAttributes();
183 final NodeList data = edge.getChildNodes();
184 final Object sourceKey = edgeAttributes.getNamedItem("source").getNodeValue();
185 final Object targetKey = edgeAttributes.getNamedItem("target").getNodeValue();
186 final ChampObject sourceObject;
187 final ChampObject targetObject;
190 final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey)).findFirst();
191 final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey)).findFirst();
193 if (!source.isPresent()) {
194 sourceObject = graph.storeObject(ChampObject.create()
198 } else sourceObject = source.get();
200 if (!target.isPresent()) {
201 targetObject = graph.storeObject(ChampObject.create()
205 } else targetObject = target.get();
207 } catch (ChampMarshallingException e) {
208 LOGGER.error("Failed to marshall object to backend type, skipping this edge", e);
210 } catch (ChampSchemaViolationException e) {
211 LOGGER.error("Source/target object violates schema constraint(s)", e);
213 } catch (ChampObjectNotExistsException e) {
214 LOGGER.error("Failed to update existing source/target ChampObject", e);
218 final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
220 for (Map<String, String> defaultProperty : edgeDefaults) {
221 champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
224 for (int k = 0; k < data.getLength(); k++) {
225 final Node datum = data.item(k);
227 if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
229 final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
230 final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
232 switch (nodePropertyDefinition.get("attr.type")) {
234 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
237 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
240 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
243 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
246 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
249 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
252 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
256 final ChampRelationship relToStore = champRelBuilder.build();
259 graph.storeRelationship(relToStore);
260 } catch (ChampMarshallingException e) {
261 LOGGER.warn("Failed to marshall ChampObject to backend type", e);
262 } catch (ChampSchemaViolationException e) {
263 LOGGER.error("Failed to store object (schema violated): " + relToStore, e);
264 } catch (ChampRelationshipNotExistsException e) {
265 LOGGER.error("Failed to update existing ChampRelationship", e);
266 } catch (ChampObjectNotExistsException e) {
267 LOGGER.error("Objects bound to relationship do not exist (should never happen)");
268 } catch (ChampUnmarshallingException e) {
269 LOGGER.error("Failed to unmarshall ChampObject to backend type");
273 private void writeNode(ChampGraph graph, Node node, Map<String, Map<String, String>> nodePropertyDefinitions, Set<Map<String, String>> nodeDefaults) {
274 final NamedNodeMap nodeAttributes = node.getAttributes();
275 final Object importAssignedId = nodeAttributes.getNamedItem("id").getNodeValue();
276 final NodeList data = node.getChildNodes();
277 final Map<String, Object> properties = new HashMap<String, Object> ();
279 for (int k = 0; k < data.getLength(); k++) {
280 final Node datum = data.item(k);
282 if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
284 final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
285 final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
287 switch (nodePropertyDefinition.get("attr.type")) {
289 properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
292 properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
295 properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
298 properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
301 properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
304 properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
307 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
311 if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
313 final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
315 for (Map<String, String> defaultProperty : nodeDefaults) {
316 champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
319 properties.remove("type");
321 champObjBuilder.properties(properties)
322 .property("importAssignedId", importAssignedId);
324 final ChampObject objectToStore = champObjBuilder.build();
327 graph.storeObject(objectToStore);
328 } catch (ChampMarshallingException e) {
329 LOGGER.warn("Failed to marshall ChampObject to backend type", e);
330 } catch (ChampSchemaViolationException e) {
331 LOGGER.error("Failed to store object (schema violated): " + objectToStore, e);
332 } catch (ChampObjectNotExistsException e) {
333 LOGGER.error("Failed to update existing ChampObject", e);
338 public void exportData(ChampGraph graph, OutputStream os) {
340 final XMLOutputFactory output = XMLOutputFactory.newInstance();
343 final XMLStreamWriter writer = output.createXMLStreamWriter(os);
345 writer.writeStartDocument();
346 writer.writeStartElement("graphml");
347 writer.writeDefaultNamespace("http://graphml.graphdrawing.org/xmlns");
348 writer.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
349 writer.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
351 final List<ChampObject> nodes = new LinkedList<ChampObject> ();
352 final List<ChampRelationship> edges = new LinkedList<ChampRelationship> ();
353 final Map<String, GraphMLKey> nodeKeys = new HashMap<String, GraphMLKey> ();
354 final Map<String, GraphMLKey> edgeKeys = new HashMap<String, GraphMLKey> ();
355 final AtomicInteger elementCount = new AtomicInteger();
357 graph.queryObjects(Collections.emptyMap()).forEach(object -> {
360 for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
361 if (nodeKeys.containsKey(property.getKey())) continue;
363 nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
366 nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
369 graph.queryRelationships(Collections.emptyMap()).forEach(relationship -> {
370 edges.add(relationship);
372 for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
373 if (nodeKeys.containsKey(property.getKey())) continue;
375 edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
378 edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
381 for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
382 final GraphMLKey graphMlKey = nodeKey.getValue();
384 writer.writeStartElement("key");
385 writer.writeAttribute("id", graphMlKey.id);
386 writer.writeAttribute("for", "node");
387 writer.writeAttribute("attr.name", graphMlKey.attrName);
388 writer.writeAttribute("attr.type", graphMlKey.attrType);
389 writer.writeEndElement();
392 for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
393 final GraphMLKey graphMlKey = edgeKey.getValue();
395 writer.writeStartElement("key");
396 writer.writeAttribute("id", graphMlKey.id);
397 writer.writeAttribute("for", "edge");
398 writer.writeAttribute("attr.name", graphMlKey.attrName);
399 writer.writeAttribute("attr.type", graphMlKey.attrType);
400 writer.writeEndElement();
403 for (ChampObject object : nodes) {
405 writer.writeStartElement("node");
406 writer.writeAttribute("id", String.valueOf(object.getKey().get()));
408 writer.writeStartElement("data");
409 writer.writeAttribute("key", nodeKeys.get("type").id);
410 writer.writeCharacters(object.getType());
411 writer.writeEndElement();
413 for (Entry<String, Object> property : object.getProperties().entrySet()) {
414 final GraphMLKey key = nodeKeys.get(property.getKey());
416 writer.writeStartElement("data");
417 writer.writeAttribute("key", key.id);
418 writer.writeCharacters(String.valueOf(property.getValue()));
419 writer.writeEndElement();
422 writer.writeEndElement();
423 } catch (XMLStreamException e) {
424 throw new RuntimeException("Failed to write edge to output stream", e);
428 for (ChampRelationship relationship : edges) {
430 writer.writeStartElement("edge");
431 writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
433 writer.writeStartElement("data");
434 writer.writeAttribute("key", edgeKeys.get("type").id);
435 writer.writeCharacters(relationship.getType());
436 writer.writeEndElement();
438 for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
439 final GraphMLKey key = edgeKeys.get(property.getKey());
441 writer.writeStartElement("data");
442 writer.writeAttribute("key", key.id);
443 writer.writeCharacters(String.valueOf(property.getValue()));
444 writer.writeEndElement();
447 writer.writeEndElement();
448 } catch (XMLStreamException e) {
449 throw new RuntimeException("Failed to write edge to output stream", e);
453 writer.writeEndElement();
454 writer.writeEndDocument();
456 } catch (XMLStreamException e) {
457 throw new RuntimeException(e);