Merge "Added @Override annotation above signature"
[aai/champ.git] / src / main / java / org / openecomp / aai / champ / ie / GraphMLImporterExporter.java
1 /**
2  * ============LICENSE_START==========================================
3  * org.onap.aai
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
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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.openecomp.aai.champ.ie;
23
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;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Optional;
35 import java.util.Set;
36 import java.util.concurrent.atomic.AtomicInteger;
37
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;
44
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;
63
64 public class GraphMLImporterExporter implements Importer, Exporter {
65
66         private static final Logger LOGGER = LoggerFactory.getLogger(GraphMLImporterExporter.class);
67
68         private static class GraphMLKey {
69                 private final String id;
70                 private final String attrName;
71                 private final String attrType;
72
73                 public GraphMLKey(String id, String attrName, Class<?> attrType) {
74                         this.id = id;
75                         this.attrName = attrName;
76
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";
89                         } else {
90                                 throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
91                         }
92                 }
93         }
94
95         public void importData(ChampAPI api, InputStream is) {
96
97                 try {
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);
102
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>> ();
107
108                         final NodeList keys = doc.getElementsByTagName("key");
109
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> ();
117
118                                 propertyDefinitions.put("attr.name", attrName);
119                                 propertyDefinitions.put("attr.type",  attrType);
120
121                                 final NodeList keyChildren = key.getChildNodes();
122
123                                 for (int j = 0; j < keyChildren.getLength(); j++) {
124                                         final Node keyChild = keyChildren.item(j);
125
126                                         if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
127
128                                         if (keyChild.getNodeName().equals("default")) {
129                                                 propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
130
131                                                 if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
132                                                 else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
133                                         }
134                                 }
135
136                                 if (elementType.equals("node")) {
137                                         nodePropertyDefinitions.put(id, propertyDefinitions);
138                                 } else if (elementType.equals("edge")) {
139                                         edgePropertyDefinitions.put(id, propertyDefinitions);
140                                 } else {
141                                         LOGGER.warn("Unknown element type {}, skipping", elementType);
142                                 }
143                         }
144
145                         final NodeList graphs = doc.getElementsByTagName("graph");
146
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();
151
152                                 api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
153                                                                                                                                                         .ofName("importAssignedId")
154                                                                                                                                                         .onAnyType()
155                                                                                                                                                         .forField("importAssignedId")
156                                                                                                                                                         .build());
157
158                                 for (int j = 0; j < nodesAndEdges.getLength(); j++) {
159                                         final Node nodeOrEdge = nodesAndEdges.item(j);
160
161                                         if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
162
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);
167                                         } else {
168                                                 LOGGER.warn("Unknown object {} found in graphML, skipping", nodeOrEdge.getNodeName());
169                                         }
170                                 }
171                         }
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);
178                 }
179         }
180
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;
188
189                 try {
190                         final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey)).findFirst();
191                         final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey)).findFirst();
192
193                         if (!source.isPresent()) {
194                                 sourceObject = graph.storeObject(ChampObject.create()
195                                                                                                                 .ofType("undefined")
196                                                                                                                 .withoutKey()
197                                                                                                                 .build());
198                         } else sourceObject = source.get();
199         
200                         if (!target.isPresent()) {
201                                 targetObject = graph.storeObject(ChampObject.create()
202                                                                                                                 .ofType("undefined")
203                                                                                                                 .withoutKey()
204                                                                                                                 .build());
205                         } else targetObject = target.get();
206
207                 } catch (ChampMarshallingException e) {
208                         LOGGER.error("Failed to marshall object to backend type, skipping this edge", e);
209                         return;
210                 } catch (ChampSchemaViolationException e) {
211                         LOGGER.error("Source/target object violates schema constraint(s)", e);
212                         return;
213                 } catch (ChampObjectNotExistsException e) {
214                         LOGGER.error("Failed to update existing source/target ChampObject", e);
215                         return;
216                 }
217
218                 final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
219
220                 for (Map<String, String> defaultProperty : edgeDefaults) {
221                         champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
222                 }
223
224                 for (int k = 0; k < data.getLength(); k++) {
225                         final Node datum = data.item(k);
226
227                         if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
228
229                         final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
230                         final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
231
232                         switch (nodePropertyDefinition.get("attr.type")) {
233                         case "boolean":
234                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
235                                 break;
236                         case "int":
237                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
238                                 break;
239                         case "long":
240                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
241                                 break;
242                         case "float":
243                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
244                                 break;
245                         case "double":
246                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
247                                 break;
248                         case "string":
249                                 champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
250                                 break;
251                         default:
252                                 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
253                         }
254                 }
255
256                 final ChampRelationship relToStore = champRelBuilder.build();
257
258                 try {
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");
270                 }
271         }
272
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> ();
278
279                 for (int k = 0; k < data.getLength(); k++) {
280                         final Node datum = data.item(k);
281
282                         if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
283
284                         final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
285                         final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
286
287                         switch (nodePropertyDefinition.get("attr.type")) {
288                         case "boolean":
289                                 properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
290                                 break;
291                         case "int":
292                                 properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
293                                 break;
294                         case "long":
295                                 properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
296                                 break;
297                         case "float":
298                                 properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
299                                 break;
300                         case "double":
301                                 properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
302                                 break;
303                         case "string":
304                                 properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
305                                 break;
306                         default:
307                                 throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
308                         }
309                 }
310
311                 if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
312
313                 final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
314
315                 for (Map<String, String> defaultProperty : nodeDefaults) {
316                         champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
317                 }
318
319                 properties.remove("type");
320
321                 champObjBuilder.properties(properties)
322                                                 .property("importAssignedId", importAssignedId);
323
324                 final ChampObject objectToStore = champObjBuilder.build();
325
326                 try {
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);
334                 }
335         }
336
337         @Override
338         public void exportData(ChampGraph graph, OutputStream os) {
339
340                 final XMLOutputFactory output = XMLOutputFactory.newInstance();
341
342                 try {
343                         final XMLStreamWriter writer = output.createXMLStreamWriter(os);
344
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");
350
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();
356
357                         graph.queryObjects(Collections.emptyMap()).forEach(object -> {
358                                 nodes.add(object);
359
360                                 for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
361                                         if (nodeKeys.containsKey(property.getKey())) continue;
362
363                                         nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
364                                 }
365
366                                 nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
367                         });
368
369                         graph.queryRelationships(Collections.emptyMap()).forEach(relationship -> {
370                                 edges.add(relationship);
371
372                                 for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
373                                         if (nodeKeys.containsKey(property.getKey())) continue;
374
375                                         edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
376                                 }
377
378                                 edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
379                         });
380
381                         for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
382                                 final GraphMLKey graphMlKey = nodeKey.getValue();
383
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();
390                         }
391
392                         for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
393                                 final GraphMLKey graphMlKey = edgeKey.getValue();
394
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();
401                         }
402
403                         for (ChampObject object : nodes) {
404                                 try {
405                                         writer.writeStartElement("node");
406                                         writer.writeAttribute("id", String.valueOf(object.getKey().get()));
407
408                                         writer.writeStartElement("data");
409                                         writer.writeAttribute("key", nodeKeys.get("type").id);
410                                         writer.writeCharacters(object.getType());
411                                         writer.writeEndElement();
412
413                                         for (Entry<String, Object> property : object.getProperties().entrySet()) {
414                                                 final GraphMLKey key = nodeKeys.get(property.getKey());
415
416                                                 writer.writeStartElement("data");
417                                                 writer.writeAttribute("key", key.id);
418                                                 writer.writeCharacters(String.valueOf(property.getValue()));
419                                                 writer.writeEndElement();
420                                         }
421
422                                         writer.writeEndElement();
423                                 } catch (XMLStreamException e) {
424                                         throw new RuntimeException("Failed to write edge to output stream", e);
425                                 }
426                         }
427
428                         for (ChampRelationship relationship : edges) {
429                                 try {
430                                         writer.writeStartElement("edge");
431                                         writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
432
433                                         writer.writeStartElement("data");
434                                         writer.writeAttribute("key", edgeKeys.get("type").id);
435                                         writer.writeCharacters(relationship.getType());
436                                         writer.writeEndElement();
437
438                                         for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
439                                                 final GraphMLKey key = edgeKeys.get(property.getKey());
440
441                                                 writer.writeStartElement("data");
442                                                 writer.writeAttribute("key", key.id);
443                                                 writer.writeCharacters(String.valueOf(property.getValue()));
444                                                 writer.writeEndElement();
445                                         }
446
447                                         writer.writeEndElement();
448                                 } catch (XMLStreamException e) {
449                                         throw new RuntimeException("Failed to write edge to output stream", e);
450                                 }
451                         }
452
453                         writer.writeEndElement();
454                         writer.writeEndDocument();
455                         writer.flush();
456                 } catch (XMLStreamException e) {
457                         throw new RuntimeException(e);
458                 }
459         }
460 }