Update license date and text
[aai/champ.git] / champ-lib / champ-core / src / main / java / org / onap / aai / champcore / ie / GraphMLImporterExporter.java
1 /**
2  * ============LICENSE_START==========================================
3  * org.onap.aai
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
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
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  */
21 package org.onap.aai.champcore.ie;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.concurrent.atomic.AtomicInteger;
36
37 import javax.xml.parsers.DocumentBuilder;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.stream.XMLOutputFactory;
41 import javax.xml.stream.XMLStreamException;
42 import javax.xml.stream.XMLStreamWriter;
43
44 import org.onap.aai.champcore.ChampAPI;
45 import org.onap.aai.champcore.ChampGraph;
46 import org.onap.aai.champcore.ChampTransaction;
47 import org.onap.aai.champcore.exceptions.ChampMarshallingException;
48 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
49 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
50 import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
51 import org.onap.aai.champcore.exceptions.ChampTransactionException;
52 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
53 import org.onap.aai.champcore.model.ChampObject;
54 import org.onap.aai.champcore.model.ChampObjectIndex;
55 import org.onap.aai.champcore.model.ChampRelationship;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.NamedNodeMap;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
64
65 public class GraphMLImporterExporter implements Importer, Exporter {
66
67         private static final Logger LOGGER = LoggerFactory.getLogger(GraphMLImporterExporter.class);
68
69         private static class GraphMLKey {
70                 private final String id;
71                 private final String attrName;
72                 private final String attrType;
73
74                 public GraphMLKey(String id, String attrName, Class<?> attrType) {
75                         this.id = id;
76                         this.attrName = attrName;
77
78                         if (attrType.equals(Boolean.class)) {
79                                 this.attrType = "boolean";
80                         } else if (attrType.equals(Integer.class)) {
81                                 this.attrType = "int";
82                         } else if (attrType.equals(Long.class)) {
83                                 this.attrType = "long";
84                         } else if (attrType.equals(Float.class)) {
85                                 this.attrType = "float";
86                         } else if (attrType.equals(Double.class)) {
87                                 this.attrType = "double";
88                         } else if (attrType.equals(String.class)) {
89                                 this.attrType = "string";
90                         } else {
91                                 throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
92                         }
93                 }
94         }
95
96         public void importData(ChampAPI api, InputStream is) {
97
98                 try {
99                         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
100                         final DocumentBuilder builder = factory.newDocumentBuilder();
101                         final InputSource inputSource = new InputSource(is);
102                     final Document doc = builder.parse(inputSource);
103
104                         final Map<String, Map<String, String>> nodePropertyDefinitions = new HashMap<String, Map<String, String>> ();
105                         final Map<String, Map<String, String>> edgePropertyDefinitions = new HashMap<String, Map<String, String>> ();
106                         final Set<Map<String, String>> nodeDefaults = new HashSet<Map<String, String>> ();
107                         final Set<Map<String, String>> edgeDefaults = new HashSet<Map<String, String>> ();
108
109                         final NodeList keys = doc.getElementsByTagName("key");
110
111                         for (int i = 0; i < keys.getLength(); i++) {
112                                 final Node key = keys.item(i);
113                                 final String id = key.getAttributes().getNamedItem("id").getNodeValue();
114                                 final String attrName = key.getAttributes().getNamedItem("attr.name").getNodeValue();
115                                 final String attrType = key.getAttributes().getNamedItem("attr.type").getNodeValue();
116                                 final String elementType = key.getAttributes().getNamedItem("for").getNodeValue();
117                                 final Map<String, String> propertyDefinitions = new HashMap<String, String> ();
118
119                                 propertyDefinitions.put("attr.name", attrName);
120                                 propertyDefinitions.put("attr.type",  attrType);
121
122                                 final NodeList keyChildren = key.getChildNodes();
123
124                                 for (int j = 0; j < keyChildren.getLength(); j++) {
125                                         final Node keyChild = keyChildren.item(j);
126
127                                         if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
128
129                                         if (keyChild.getNodeName().equals("default")) {
130                                                 propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
131
132                                                 if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
133                                                 else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
134                                         }
135                                 }
136
137                                 if (elementType.equals("node")) {
138                                         nodePropertyDefinitions.put(id, propertyDefinitions);
139                                 } else if (elementType.equals("edge")) {
140                                         edgePropertyDefinitions.put(id, propertyDefinitions);
141                                 } else {
142                                         LOGGER.warn("Unknown element type {}, skipping", elementType);
143                                 }
144                         }
145
146                         final NodeList graphs = doc.getElementsByTagName("graph");
147
148                         for (int i = 0; i < graphs.getLength(); i++) {
149                                 final Node graph = graphs.item(i);
150                                 final String graphName = graph.getAttributes().getNamedItem("id").getNodeValue();
151                                 final NodeList nodesAndEdges = graph.getChildNodes();
152
153                                 api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
154                                                                                                                                                         .ofName("importAssignedId")
155                                                                                                                                                         .onAnyType()
156                                                                                                                                                         .forField("importAssignedId")
157                                                                                                                                                         .build());
158
159                                 for (int j = 0; j < nodesAndEdges.getLength(); j++) {
160                                         final Node nodeOrEdge = nodesAndEdges.item(j);
161
162                                         if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
163
164                                         if (nodeOrEdge.getNodeName().equals("node")) {
165                                                 writeNode(api.getGraph(graphName), nodeOrEdge, nodePropertyDefinitions, nodeDefaults);
166                                         } else if (nodeOrEdge.getNodeName().equals("edge")) {
167                                                 writeEdge(api.getGraph(graphName), nodeOrEdge, edgePropertyDefinitions, edgeDefaults);
168                                         } else {
169                                                 LOGGER.warn("Unknown object {} found in graphML, skipping", nodeOrEdge.getNodeName());
170                                         }
171                                 }
172                         }
173                 } catch (ParserConfigurationException e) {
174                         throw new RuntimeException("Failed to setup DocumentBuilder", e);
175                 } catch (SAXException e) {
176                         throw new RuntimeException("Failed to parse input stream", e);
177                 } catch (IOException e) {
178                         throw new RuntimeException("Failed to parse input stream", e);
179                 }
180         }
181
182         private void writeEdge(ChampGraph graph, Node edge, Map<String, Map<String, String>> edgePropertyDefinitions, Set<Map<String, String>> edgeDefaults) {
183                 final NamedNodeMap edgeAttributes = edge.getAttributes();
184                 final NodeList data = edge.getChildNodes();
185                 final Object sourceKey = edgeAttributes.getNamedItem("source").getNodeValue();
186                 final Object targetKey = edgeAttributes.getNamedItem("target").getNodeValue();
187                 ChampObject sourceObject=null;
188                 ChampObject targetObject=null;
189                 
190                 try {
191                         final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey), Optional.empty()).findFirst();
192                         final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey), Optional.empty()).findFirst();
193
194                         if (!source.isPresent()) {
195                                 sourceObject = graph.storeObject(ChampObject.create()
196                                                                                                                 .ofType("undefined")
197                                                                                                                 .withoutKey()
198                                                                                                                 .build(),
199                                                                                                  Optional.empty());
200                         } else sourceObject = source.get();
201         
202                         if (!target.isPresent()) {
203                                 targetObject = graph.storeObject(ChampObject.create()
204                                                                                                                 .ofType("undefined")
205                                                                                                                 .withoutKey()
206                                                                                                                 .build(),
207                                                                                                  Optional.empty());
208                         } else targetObject = target.get();
209
210                 } catch (ChampMarshallingException e) {
211                         LOGGER.error("Failed to marshall object to backend type, skipping this edge", e);
212                         return;
213                 } catch (ChampSchemaViolationException e) {
214                         LOGGER.error("Source/target object violates schema constraint(s)", e);
215                         return;
216                 } catch (ChampObjectNotExistsException e) {
217                         LOGGER.error("Failed to update existing source/target ChampObject", e);
218                         return;
219                 } catch (ChampTransactionException e) {
220             LOGGER.error("Failed to commit or rollback transaction", e);
221                 }
222
223                 final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
224
225                 for (Map<String, String> defaultProperty : edgeDefaults) {
226                         champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
227                 }
228
229                 for (int k = 0; k < data.getLength(); k++) {
230                         final Node datum = data.item(k);
231
232                         if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
233
234                         final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
235                         final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
236
237                         switch (nodePropertyDefinition.get("attr.type")) {
238                         case "boolean":
239                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
240                                 break;
241                         case "int":
242                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
243                                 break;
244                         case "long":
245                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
246                                 break;
247                         case "float":
248                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
249                                 break;
250                         case "double":
251                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
252                                 break;
253                         case "string":
254                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
255                                 break;
256                         default:
257                                 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
258                         }
259                 }
260
261                 final ChampRelationship relToStore = champRelBuilder.build();
262
263                 try {
264                         graph.storeRelationship(relToStore, Optional.empty());
265                 } catch (ChampMarshallingException e) {
266                         LOGGER.warn("Failed to marshall ChampObject to backend type", e);
267                 } catch (ChampSchemaViolationException e) {
268                         LOGGER.error("Failed to store object (schema violated): " + relToStore, e);
269                 } catch (ChampRelationshipNotExistsException e) {
270                         LOGGER.error("Failed to update existing ChampRelationship", e);
271                 } catch (ChampObjectNotExistsException e) {
272                         LOGGER.error("Objects bound to relationship do not exist (should never happen)");
273                 } catch (ChampUnmarshallingException e) {
274                         LOGGER.error("Failed to unmarshall ChampObject to backend type");
275                 } catch (ChampTransactionException e) {
276                     LOGGER.error("Failed to commit or rollback transaction");
277                 }
278                 
279         }
280
281         private void writeNode(ChampGraph graph, Node node, Map<String, Map<String, String>> nodePropertyDefinitions, Set<Map<String, String>> nodeDefaults) {
282                 final NamedNodeMap nodeAttributes = node.getAttributes();
283                 final Object importAssignedId = nodeAttributes.getNamedItem("id").getNodeValue();
284                 final NodeList data = node.getChildNodes();
285                 final Map<String, Object> properties = new HashMap<String, Object> ();
286
287                 for (int k = 0; k < data.getLength(); k++) {
288                         final Node datum = data.item(k);
289
290                         if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
291
292                         final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
293                         final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
294
295                         switch (nodePropertyDefinition.get("attr.type")) {
296                         case "boolean":
297                                 properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
298                                 break;
299                         case "int":
300                                 properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
301                                 break;
302                         case "long":
303                                 properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
304                                 break;
305                         case "float":
306                                 properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
307                                 break;
308                         case "double":
309                                 properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
310                                 break;
311                         case "string":
312                                 properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
313                                 break;
314                         default:
315                                 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
316                         }
317                 }
318
319                 if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
320
321                 final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
322
323                 for (Map<String, String> defaultProperty : nodeDefaults) {
324                         champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
325                 }
326
327                 properties.remove("type");
328
329                 champObjBuilder.properties(properties)
330                                                 .property("importAssignedId", importAssignedId);
331
332                 final ChampObject objectToStore = champObjBuilder.build();
333
334                 try {  
335                   graph.storeObject(objectToStore, Optional.empty());
336                 } catch (ChampMarshallingException e) {
337                         LOGGER.warn("Failed to marshall ChampObject to backend type", e);
338                 } catch (ChampSchemaViolationException e) {
339                         LOGGER.error("Failed to store object (schema violated): " + objectToStore, e);
340                 } catch (ChampObjectNotExistsException e) {
341                         LOGGER.error("Failed to update existing ChampObject", e);
342                 } catch (ChampTransactionException e) {
343           LOGGER.error("Failed to commit or rollback transaction");
344         }
345         }
346
347         @Override
348         public void exportData(ChampGraph graph, OutputStream os) {
349
350                 final XMLOutputFactory output = XMLOutputFactory.newInstance();
351
352                 try {
353                         final XMLStreamWriter writer = output.createXMLStreamWriter(os);
354
355                         writer.writeStartDocument();
356                         writer.writeStartElement("graphml");
357                         writer.writeDefaultNamespace("http://graphml.graphdrawing.org/xmlns");
358                         writer.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
359                         writer.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
360
361                         final List<ChampObject> nodes = new LinkedList<ChampObject> ();
362                         final List<ChampRelationship> edges = new LinkedList<ChampRelationship> ();
363                         final Map<String, GraphMLKey> nodeKeys = new HashMap<String, GraphMLKey> ();
364                         final Map<String, GraphMLKey> edgeKeys = new HashMap<String, GraphMLKey> ();
365                         final AtomicInteger elementCount = new AtomicInteger();
366
367                         graph.queryObjects(Collections.emptyMap(), Optional.empty()).forEach(object -> {
368                                 nodes.add(object);
369
370                                 for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
371                                         if (nodeKeys.containsKey(property.getKey())) continue;
372
373                                         nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
374                                 }
375
376                                 nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
377                         });
378
379                         graph.queryRelationships(Collections.emptyMap(), Optional.empty()).forEach(relationship -> {
380                                 edges.add(relationship);
381
382                                 for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
383                                         if (nodeKeys.containsKey(property.getKey())) continue;
384
385                                         edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
386                                 }
387
388                                 edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
389                         });
390
391                         for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
392                                 final GraphMLKey graphMlKey = nodeKey.getValue();
393
394                                 writer.writeStartElement("key");
395                                 writer.writeAttribute("id", graphMlKey.id);
396                                 writer.writeAttribute("for", "node");
397                                 writer.writeAttribute("attr.name", graphMlKey.attrName);
398                                 writer.writeAttribute("attr.type", graphMlKey.attrType);
399                                 writer.writeEndElement();
400                         }
401
402                         for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
403                                 final GraphMLKey graphMlKey = edgeKey.getValue();
404
405                                 writer.writeStartElement("key");
406                                 writer.writeAttribute("id", graphMlKey.id);
407                                 writer.writeAttribute("for", "edge");
408                                 writer.writeAttribute("attr.name", graphMlKey.attrName);
409                                 writer.writeAttribute("attr.type", graphMlKey.attrType);
410                                 writer.writeEndElement();
411                         }
412
413                         for (ChampObject object : nodes) {
414                                 try {
415                                         writer.writeStartElement("node");
416                                         writer.writeAttribute("id", String.valueOf(object.getKey().get()));
417
418                                         writer.writeStartElement("data");
419                                         writer.writeAttribute("key", nodeKeys.get("type").id);
420                                         writer.writeCharacters(object.getType());
421                                         writer.writeEndElement();
422
423                                         for (Entry<String, Object> property : object.getProperties().entrySet()) {
424                                                 final GraphMLKey key = nodeKeys.get(property.getKey());
425
426                                                 writer.writeStartElement("data");
427                                                 writer.writeAttribute("key", key.id);
428                                                 writer.writeCharacters(String.valueOf(property.getValue()));
429                                                 writer.writeEndElement();
430                                         }
431
432                                         writer.writeEndElement();
433                                 } catch (XMLStreamException e) {
434                                         throw new RuntimeException("Failed to write edge to output stream", e);
435                                 }
436                         }
437
438                         for (ChampRelationship relationship : edges) {
439                                 try {
440                                         writer.writeStartElement("edge");
441                                         writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
442
443                                         writer.writeStartElement("data");
444                                         writer.writeAttribute("key", edgeKeys.get("type").id);
445                                         writer.writeCharacters(relationship.getType());
446                                         writer.writeEndElement();
447
448                                         for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
449                                                 final GraphMLKey key = edgeKeys.get(property.getKey());
450
451                                                 writer.writeStartElement("data");
452                                                 writer.writeAttribute("key", key.id);
453                                                 writer.writeCharacters(String.valueOf(property.getValue()));
454                                                 writer.writeEndElement();
455                                         }
456
457                                         writer.writeEndElement();
458                                 } catch (XMLStreamException e) {
459                                         throw new RuntimeException("Failed to write edge to output stream", e);
460                                 }
461                         }
462
463                         writer.writeEndElement();
464                         writer.writeEndDocument();
465                         writer.flush();
466                 } catch (XMLStreamException | ChampTransactionException e) {
467                         throw new RuntimeException(e);
468                 }
469         }
470 }