2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 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============================================
21 package org.onap.aai.champcore.ie;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.util.ArrayList;
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.onap.aai.champcore.ChampAPI;
46 import org.onap.aai.champcore.ChampCoreMsgs;
47 import org.onap.aai.champcore.ChampGraph;
48 import org.onap.aai.champcore.exceptions.ChampMarshallingException;
49 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
50 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
51 import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
52 import org.onap.aai.champcore.exceptions.ChampTransactionException;
53 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
54 import org.onap.aai.champcore.model.ChampObject;
55 import org.onap.aai.champcore.model.ChampObjectIndex;
56 import org.onap.aai.champcore.model.ChampRelationship;
57 import org.onap.aai.cl.api.Logger;
58 import org.onap.aai.cl.eelf.LoggerFactory;
59 import org.w3c.dom.Document;
60 import org.w3c.dom.NamedNodeMap;
61 import org.w3c.dom.Node;
62 import org.w3c.dom.NodeList;
63 import org.xml.sax.InputSource;
64 import org.xml.sax.SAXException;
66 public class GraphMLImporterExporter implements Importer, Exporter {
68 private static final Logger LOGGER = LoggerFactory.getInstance().getLogger(GraphMLImporterExporter.class);
70 private static class GraphMLKey {
71 private final String id;
72 private final String attrName;
73 private final String attrType;
75 public GraphMLKey(String id, String attrName, Class<?> attrType) {
77 this.attrName = attrName;
79 if (attrType.equals(Boolean.class)) {
80 this.attrType = "boolean";
81 } else if (attrType.equals(Integer.class)) {
82 this.attrType = "int";
83 } else if (attrType.equals(Long.class)) {
84 this.attrType = "long";
85 } else if (attrType.equals(Float.class)) {
86 this.attrType = "float";
87 } else if (attrType.equals(Double.class)) {
88 this.attrType = "double";
89 } else if (attrType.equals(String.class)) {
90 this.attrType = "string";
92 throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
97 public void importData(ChampAPI api, InputStream is) {
100 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
101 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
102 final DocumentBuilder builder = factory.newDocumentBuilder();
103 final InputSource inputSource = new InputSource(is);
104 final Document doc = builder.parse(inputSource);
106 final Map<String, Map<String, String>> nodePropertyDefinitions = new HashMap<String, Map<String, String>> ();
107 final Map<String, Map<String, String>> edgePropertyDefinitions = new HashMap<String, Map<String, String>> ();
108 final Set<Map<String, String>> nodeDefaults = new HashSet<Map<String, String>> ();
109 final Set<Map<String, String>> edgeDefaults = new HashSet<Map<String, String>> ();
111 final NodeList keys = doc.getElementsByTagName("key");
113 for (int i = 0; i < keys.getLength(); i++) {
114 final Node key = keys.item(i);
115 final String id = key.getAttributes().getNamedItem("id").getNodeValue();
116 final String attrName = key.getAttributes().getNamedItem("attr.name").getNodeValue();
117 final String attrType = key.getAttributes().getNamedItem("attr.type").getNodeValue();
118 final String elementType = key.getAttributes().getNamedItem("for").getNodeValue();
119 final Map<String, String> propertyDefinitions = new HashMap<String, String> ();
121 propertyDefinitions.put("attr.name", attrName);
122 propertyDefinitions.put("attr.type", attrType);
124 final NodeList keyChildren = key.getChildNodes();
126 for (int j = 0; j < keyChildren.getLength(); j++) {
127 final Node keyChild = keyChildren.item(j);
129 if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
131 if (keyChild.getNodeName().equals("default")) {
132 propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
134 if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
135 else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
139 if (elementType.equals("node")) {
140 nodePropertyDefinitions.put(id, propertyDefinitions);
141 } else if (elementType.equals("edge")) {
142 edgePropertyDefinitions.put(id, propertyDefinitions);
144 LOGGER.warn(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_WARN,
145 String.format("Unknown element type %s, skipping", elementType));
149 final NodeList graphs = doc.getElementsByTagName("graph");
151 for (int i = 0; i < graphs.getLength(); i++) {
152 final Node graph = graphs.item(i);
153 final String graphName = graph.getAttributes().getNamedItem("id").getNodeValue();
154 final NodeList nodesAndEdges = graph.getChildNodes();
156 List<String> fields = new ArrayList<String>();
157 fields.add("importAssignedId");
158 api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
159 .ofName("importAssignedId")
164 for (int j = 0; j < nodesAndEdges.getLength(); j++) {
165 final Node nodeOrEdge = nodesAndEdges.item(j);
167 if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
169 if (nodeOrEdge.getNodeName().equals("node")) {
170 writeNode(api.getGraph(graphName), nodeOrEdge, nodePropertyDefinitions, nodeDefaults);
171 } else if (nodeOrEdge.getNodeName().equals("edge")) {
172 writeEdge(api.getGraph(graphName), nodeOrEdge, edgePropertyDefinitions, edgeDefaults);
174 LOGGER.warn(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_WARN,
175 String.format("Unknown object %s found in graphML, skipping", nodeOrEdge.getNodeName()));
179 } catch (ParserConfigurationException e) {
180 throw new RuntimeException("Failed to setup DocumentBuilder", e);
181 } catch (SAXException e) {
182 throw new RuntimeException("Failed to parse input stream", e);
183 } catch (IOException e) {
184 throw new RuntimeException("Failed to parse input stream", e);
188 private void writeEdge(ChampGraph graph, Node edge, Map<String, Map<String, String>> edgePropertyDefinitions, Set<Map<String, String>> edgeDefaults) {
189 final NamedNodeMap edgeAttributes = edge.getAttributes();
190 final NodeList data = edge.getChildNodes();
191 final Object sourceKey = edgeAttributes.getNamedItem("source").getNodeValue();
192 final Object targetKey = edgeAttributes.getNamedItem("target").getNodeValue();
193 ChampObject sourceObject=null;
194 ChampObject targetObject=null;
197 final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey), Optional.empty()).findFirst();
198 final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey), Optional.empty()).findFirst();
200 if (!source.isPresent()) {
201 sourceObject = graph.storeObject(ChampObject.create()
206 } else sourceObject = source.get();
208 if (!target.isPresent()) {
209 targetObject = graph.storeObject(ChampObject.create()
214 } else targetObject = target.get();
216 } catch (ChampMarshallingException e) {
217 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
218 "Failed to marshall object to backend type, skipping this edge. " + e.getMessage());
220 } catch (ChampSchemaViolationException e) {
221 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
222 "Source/target object violates schema constraint(s). " + e.getMessage());
224 } catch (ChampObjectNotExistsException e) {
225 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
226 "Failed to update existing source/target ChampObject. " + e.getMessage());
228 } catch (ChampTransactionException e) {
229 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
230 "Failed to commit or rollback transaction. " + e.getMessage());
233 final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
235 for (Map<String, String> defaultProperty : edgeDefaults) {
236 champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
239 for (int k = 0; k < data.getLength(); k++) {
240 final Node datum = data.item(k);
242 if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
244 final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
245 final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
247 switch (nodePropertyDefinition.get("attr.type")) {
249 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
252 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
255 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
258 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
261 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
264 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
267 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
271 final ChampRelationship relToStore = champRelBuilder.build();
274 graph.storeRelationship(relToStore, Optional.empty());
275 } catch (ChampMarshallingException e) {
276 LOGGER.warn(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_WARN,
277 "Failed to marshall ChampObject to backend type. " + e.getMessage());
278 } catch (ChampSchemaViolationException e) {
279 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
280 "Failed to store object (schema violated): " + relToStore + " " + e.getMessage());
281 } catch (ChampRelationshipNotExistsException e) {
282 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
283 "Failed to update existing ChampRelationship. " + e.getMessage());
284 } catch (ChampObjectNotExistsException e) {
285 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
286 "Objects bound to relationship do not exist (should never happen)");
287 } catch (ChampUnmarshallingException e) {
288 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
289 "Failed to unmarshall ChampObject to backend type");
290 } catch (ChampTransactionException e) {
291 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
292 "Failed to commit or rollback transaction");
297 private void writeNode(ChampGraph graph, Node node, Map<String, Map<String, String>> nodePropertyDefinitions, Set<Map<String, String>> nodeDefaults) {
298 final NamedNodeMap nodeAttributes = node.getAttributes();
299 final Object importAssignedId = nodeAttributes.getNamedItem("id").getNodeValue();
300 final NodeList data = node.getChildNodes();
301 final Map<String, Object> properties = new HashMap<String, Object> ();
303 for (int k = 0; k < data.getLength(); k++) {
304 final Node datum = data.item(k);
306 if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
308 final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
309 final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
311 switch (nodePropertyDefinition.get("attr.type")) {
313 properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
316 properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
319 properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
322 properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
325 properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
328 properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
331 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
335 if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
337 final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
339 for (Map<String, String> defaultProperty : nodeDefaults) {
340 champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
343 properties.remove("type");
345 champObjBuilder.properties(properties)
346 .property("importAssignedId", importAssignedId);
348 final ChampObject objectToStore = champObjBuilder.build();
351 graph.storeObject(objectToStore, Optional.empty());
352 } catch (ChampMarshallingException e) {
353 LOGGER.warn(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_WARN,
354 "Failed to marshall ChampObject to backend type. " + e.getMessage());
355 } catch (ChampSchemaViolationException e) {
356 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
357 "Failed to store object (schema violated): " + objectToStore + " " + e.getMessage());
358 } catch (ChampObjectNotExistsException e) {
359 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
360 "Failed to update existing ChampObject. " + e.getMessage());
361 } catch (ChampTransactionException e) {
362 LOGGER.error(ChampCoreMsgs.CHAMPCORE_GRAPH_ML_IMPORTER_EXPORTER_ERROR,
363 "Failed to commit or rollback transaction");
368 public void exportData(ChampGraph graph, OutputStream os) {
370 final XMLOutputFactory output = XMLOutputFactory.newInstance();
373 final XMLStreamWriter writer = output.createXMLStreamWriter(os);
375 writer.writeStartDocument();
376 writer.writeStartElement("graphml");
377 writer.writeDefaultNamespace("http://graphml.graphdrawing.org/xmlns");
378 writer.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
379 writer.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
381 final List<ChampObject> nodes = new LinkedList<ChampObject> ();
382 final List<ChampRelationship> edges = new LinkedList<ChampRelationship> ();
383 final Map<String, GraphMLKey> nodeKeys = new HashMap<String, GraphMLKey> ();
384 final Map<String, GraphMLKey> edgeKeys = new HashMap<String, GraphMLKey> ();
385 final AtomicInteger elementCount = new AtomicInteger();
387 graph.queryObjects(Collections.emptyMap(), Optional.empty()).forEach(object -> {
390 for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
391 if (nodeKeys.containsKey(property.getKey())) continue;
393 nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
396 nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
399 graph.queryRelationships(Collections.emptyMap(), Optional.empty()).forEach(relationship -> {
400 edges.add(relationship);
402 for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
403 if (nodeKeys.containsKey(property.getKey())) continue;
405 edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
408 edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
411 for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
412 final GraphMLKey graphMlKey = nodeKey.getValue();
414 writer.writeStartElement("key");
415 writer.writeAttribute("id", graphMlKey.id);
416 writer.writeAttribute("for", "node");
417 writer.writeAttribute("attr.name", graphMlKey.attrName);
418 writer.writeAttribute("attr.type", graphMlKey.attrType);
419 writer.writeEndElement();
422 for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
423 final GraphMLKey graphMlKey = edgeKey.getValue();
425 writer.writeStartElement("key");
426 writer.writeAttribute("id", graphMlKey.id);
427 writer.writeAttribute("for", "edge");
428 writer.writeAttribute("attr.name", graphMlKey.attrName);
429 writer.writeAttribute("attr.type", graphMlKey.attrType);
430 writer.writeEndElement();
433 for (ChampObject object : nodes) {
435 writer.writeStartElement("node");
436 writer.writeAttribute("id", String.valueOf(object.getKey().get()));
438 writer.writeStartElement("data");
439 writer.writeAttribute("key", nodeKeys.get("type").id);
440 writer.writeCharacters(object.getType());
441 writer.writeEndElement();
443 for (Entry<String, Object> property : object.getProperties().entrySet()) {
444 final GraphMLKey key = nodeKeys.get(property.getKey());
446 writer.writeStartElement("data");
447 writer.writeAttribute("key", key.id);
448 writer.writeCharacters(String.valueOf(property.getValue()));
449 writer.writeEndElement();
452 writer.writeEndElement();
453 } catch (XMLStreamException e) {
454 throw new RuntimeException("Failed to write edge to output stream", e);
458 for (ChampRelationship relationship : edges) {
460 writer.writeStartElement("edge");
461 writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
463 writer.writeStartElement("data");
464 writer.writeAttribute("key", edgeKeys.get("type").id);
465 writer.writeCharacters(relationship.getType());
466 writer.writeEndElement();
468 for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
469 final GraphMLKey key = edgeKeys.get(property.getKey());
471 writer.writeStartElement("data");
472 writer.writeAttribute("key", key.id);
473 writer.writeCharacters(String.valueOf(property.getValue()));
474 writer.writeEndElement();
477 writer.writeEndElement();
478 } catch (XMLStreamException e) {
479 throw new RuntimeException("Failed to write edge to output stream", e);
483 writer.writeEndElement();
484 writer.writeEndDocument();
486 } catch (XMLStreamException | ChampTransactionException e) {
487 throw new RuntimeException(e);