Refactor to move from openecomp to onap
[aai/gizmo.git] / src / main / java / org / onap / crud / dao / champ / ChampDao.java
1 /**
2  * ============LICENSE_START=======================================================
3  * Gizmo
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  *
22  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23  */
24 package org.onap.crud.dao.champ;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33
34 import org.onap.aai.champcore.ChampGraph;
35 import org.onap.aai.champcore.ChampTransaction;
36 import org.onap.aai.champcore.exceptions.ChampMarshallingException;
37 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
38 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
39 import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
40 import org.onap.aai.champcore.exceptions.ChampTransactionException;
41 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
42 import org.onap.aai.champcore.model.ChampObject;
43 import org.onap.aai.champcore.model.ChampRelationship;
44 import org.onap.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep;
45 import org.onap.aai.cl.api.Logger;
46 import org.onap.aai.cl.eelf.LoggerFactory;
47 import org.onap.crud.dao.GraphDao;
48 import org.onap.crud.entity.Edge;
49 import org.onap.crud.entity.Vertex;
50 import org.onap.crud.exception.CrudException;
51 import org.onap.crud.logging.CrudServiceMsgs;
52
53 /**
54  * This is the integration layer between the CRUD API service and the low level
55  * Champ library for graph database interaction.
56  */
57 public class ChampDao implements GraphDao {
58
59   public static final String CONFIG_STORAGE_BACKEND = "storage.backend";
60   public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db";
61   public static final String STORAGE_HBASE_DB = "hbase";
62   public static final String STORAGE_CASSANDRA_DB = "cassandra";
63   public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames";
64   public static final String CONFIG_STORAGE_PORT = "storage.port";
65   public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent";
66   public static final String CONFIG_GRAPH_NAME = "graph.name";
67   public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix";
68
69   public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher";
70   public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers";
71
72   private static Map<String, ChampTransaction> transactions = new ConcurrentHashMap<String, ChampTransaction>();
73   public static final String DEFAULT_GRAPH_NAME = "default_graph";
74
75   private enum GraphType {
76     IN_MEMORY, TITAN, DSE
77   }
78
79   /**
80    * Instance of the API used for interacting with the Champ library.
81    */
82   private ChampGraph champApi = null;
83
84   private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName());
85
86   /**
87    * Creates a new instance of the ChampDao.
88    *
89    * @param champGraph
90    *          - Concrete implementation of the graph dao layer
91    */
92   public ChampDao(ChampGraph champGraph) {
93     this.champApi = champGraph;
94   }
95
96   @Override
97   public Vertex getVertex(String id) throws CrudException {
98
99     try {
100
101       if (logger.isDebugEnabled()) {
102         logger.debug("getVertex with id: " + id);
103       }
104
105       long idAsLong = Long.parseLong(id);
106
107       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
108
109       String nodeType = org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName();
110       if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties().get(nodeType) != null) {
111         return vertexFromChampObject(retrievedVertex.get(),
112             retrievedVertex.get().getProperties().get(nodeType).toString());
113       } else {
114
115         // We didn't find a vertex with the supplied id, so just throw an
116         // exception.
117         throw new CrudException("No vertex with id " + id + " found in graph",
118             javax.ws.rs.core.Response.Status.NOT_FOUND);
119       }
120
121     } catch (ChampUnmarshallingException | ChampTransactionException e) {
122
123       // Something went wrong - throw an exception.
124       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
125     }
126   }
127
128   @Override
129   public Vertex getVertex(String id, String type) throws CrudException {
130
131     try {
132
133       if (logger.isDebugEnabled()) {
134         logger.debug("getVertex with id: " + id);
135       }
136
137       long idAsLong = Long.parseLong(id);
138
139       // Request the vertex from the graph db.
140       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
141
142       // Did we find it?
143       if (retrievedVertex.isPresent()
144           && retrievedVertex.get().getProperties()
145               .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) != null
146           && retrievedVertex.get().getProperties()
147               .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
148               .equalsIgnoreCase(type)) {
149
150         // Yup, convert it to a Vector object and return it.
151         return vertexFromChampObject(retrievedVertex.get(), type);
152
153       } else {
154
155         // We didn't find a vertex with the supplied id, so just throw an
156         // exception.
157         throw new CrudException("No vertex with id " + id + " found in graph",
158             javax.ws.rs.core.Response.Status.NOT_FOUND);
159       }
160
161     } catch (ChampUnmarshallingException | ChampTransactionException e) {
162
163       // Something went wrong - throw an exception.
164       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
165     }
166   }
167
168   @Override
169   public List<Edge> getVertexEdges(String id) throws CrudException {
170
171     if (logger.isDebugEnabled()) {
172       logger.debug("get Edges incident to vertex with id: " + id + " from graph");
173     }
174
175     try {
176       long idAsLong = Long.parseLong(id); // GDF - what to do about id???
177
178       // Request the vertex from the graph db.
179       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
180
181       // Did we find it?
182       if (retrievedVertex.isPresent()) {
183
184         // Query the Champ library for the edges which are incident to the
185         // specified
186         // vertex.
187         Stream<ChampRelationship> relationships = champApi.retrieveRelationships(retrievedVertex.get());
188
189         // Build an edge list from the result stream.
190         List<Edge> edges = new ArrayList<Edge>();
191         relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
192
193         return edges;
194
195       } else {
196
197         // We couldn't find the specified vertex, so throw an exception.
198         throw new CrudException("No vertex with id " + id + " found in graph",
199             javax.ws.rs.core.Response.Status.NOT_FOUND);
200       }
201
202     } catch (ChampUnmarshallingException e) {
203
204       // Something went wrong, so throw an exception.
205       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
206
207     } catch (ChampObjectNotExistsException e) {
208
209       // We couldn't find the specified vertex, so throw an exception.
210       throw new CrudException("No vertex with id " + id + " found in graph",
211           javax.ws.rs.core.Response.Status.NOT_FOUND);
212     } catch (ChampTransactionException e) {
213       throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
214     }
215   }
216
217   @Override
218   public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
219
220     if (logger.isDebugEnabled()) {
221       logger.debug("Add/update vertex: {label: " + type + " properties:" + propertiesMapToString(properties));
222     }
223
224     // Add the aai_node_type so that AAI can read the data created by gizmo
225     properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
226
227     // Create an object to represent our vertex in the format expected by the
228     // Champ library.
229     ChampObject objectToCreate = buildChampObject(type, properties);
230
231     try {
232
233       // Ask the Champ library to store our vertex, placing the returned object
234       // into a
235       // list so that we can easily put that into our result object.
236       return vertexFromChampObject(champApi.storeObject(objectToCreate), type);
237
238     } catch (ChampMarshallingException | ChampSchemaViolationException | ChampObjectNotExistsException
239         | ChampTransactionException e) {
240
241       // Something went wrong - throw an exception.
242       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
243     }
244   }
245
246   @Override
247   public Vertex updateVertex(String id, String type, Map<String, Object> properties) throws CrudException {
248
249     if (logger.isDebugEnabled()) {
250       logger.debug("Update vertex with id: " + id + " with properties: " + propertiesMapToString(properties));
251     }
252     // Add the aai_node_type so that AAI can read the data created by gizmo
253     properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
254
255     try {
256       // Now, build the updated version of the Champ Object...
257       ChampObject updateObject = buildChampObject(id, type, properties);
258       // ...and send it to the Champ library.
259       return vertexFromChampObject(champApi.replaceObject(updateObject), type);
260
261     } catch (ChampObjectNotExistsException e) {
262       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
263     } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
264       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
265     } catch (ChampTransactionException e) {
266       throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
267     }
268
269   }
270
271   @Override
272   public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
273
274     if (logger.isDebugEnabled()) {
275       logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
276           + propertiesMapToString(filter));
277     }
278
279     filter.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
280
281     Stream<ChampObject> retrievedVertices;
282     try {
283       retrievedVertices = champApi.queryObjects(filter);
284
285     } catch (ChampTransactionException e) {
286       throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
287     }
288
289     List<Vertex> vertices = retrievedVertices.map(v -> vertexFromChampObject(v, type)).collect(Collectors.toList());
290
291     if (logger.isDebugEnabled()) {
292       logger.debug("Resulting vertex list: " + retrievedVertices);
293     }
294
295     // ...and return it to the caller.
296     return vertices;
297   }
298
299   private Object getRelKey(String id) {
300     Object key = id;
301     // convert into Long if applicable . TODO : revisit in story NUC-304
302     try {
303       key = Long.parseLong(id);
304     } catch (NumberFormatException e) {
305       // The id isn't a Long, leave it as a string
306     }
307
308     return key;
309   }
310
311   @Override
312   public Edge getEdge(String id, String type) throws CrudException {
313
314     if (logger.isDebugEnabled()) {
315       logger.debug("Get edge with id: " + id);
316     }
317
318     try {
319
320       // Request the edge from the graph db.
321       Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
322
323       // Did we find it?
324       if (relationship.isPresent() && relationship.get().getType().equals(type)) {
325
326         // Yup - return the result.
327         return edgeFromChampRelationship(relationship.get());
328
329       } else {
330
331         // We didn't find an edge with the supplied id, so throw an exception.
332         throw new CrudException("No edge with id " + id + " found in graph",
333             javax.ws.rs.core.Response.Status.NOT_FOUND);
334       }
335
336     } catch (ChampUnmarshallingException | ChampTransactionException e) {
337
338       // Something went wrong, so throw an exception.
339       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
340     }
341   }
342
343   @Override
344   public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties) throws CrudException {
345
346     // For now, assume source and target are straight ids...
347     try {
348
349       Optional<ChampObject> sourceObject = champApi.retrieveObject(Long.parseLong(source.getId().get()));
350       if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
351         throw new CrudException(
352             "Error creating edge - source vertex with id " + source + " does not exist in graph data base",
353             javax.ws.rs.core.Response.Status.BAD_REQUEST);
354       }
355
356       Optional<ChampObject> targetObject = champApi.retrieveObject(Long.parseLong(target.getId().get()));
357       if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
358         throw new CrudException(
359             "Error creating edge - target vertex with id " + target + " does not exist in graph data base",
360             javax.ws.rs.core.Response.Status.BAD_REQUEST);
361       }
362
363       // Now, create the ChampRelationship object for our edge and store it in
364       // the graph database.
365       return edgeFromChampRelationship(champApi.storeRelationship(
366           new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type).properties(properties).build()));
367
368     } catch (ChampMarshallingException | ChampObjectNotExistsException | ChampSchemaViolationException
369         | ChampRelationshipNotExistsException | ChampUnmarshallingException | NumberFormatException
370         | ChampTransactionException e) {
371
372       throw new CrudException("Error creating edge: " + e.getMessage(),
373           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
374     }
375   }
376
377   @Override
378   public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
379
380     filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
381
382     Stream<ChampRelationship> retrievedRelationships;
383     try {
384       retrievedRelationships = champApi.queryRelationships(filter);
385
386     } catch (ChampTransactionException e) {
387       throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
388     }
389
390     // Process the result stream from the Champ library into an Edge list,
391     // keeping only
392     // edges of the specified type.
393     List<Edge> edges = retrievedRelationships.map(r -> edgeFromChampRelationship(r)).collect(Collectors.toList());
394
395     return edges;
396   }
397
398   @Override
399   public Edge updateEdge(Edge edge) throws CrudException {
400
401     if (logger.isDebugEnabled()) {
402       logger.debug(
403           "Update edge with id: " + edge.getId() + " with properties: " + propertiesMapToString(edge.getProperties()));
404     }
405
406     try {
407       // Now, build the updated version of the Champ Relationship...
408       ChampRelationship updateRelationship = new ChampRelationship.Builder(
409           buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
410               edge.getSource().getProperties()),
411           buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
412               edge.getTarget().getProperties()),
413           edge.getType()).key(getRelKey(edge.getId().get())).properties(edge.getProperties()).build();
414       // ...and send it to the Champ library.
415       return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
416
417     } catch (ChampRelationshipNotExistsException ex) {
418       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
419     } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException
420         | ChampSchemaViolationException | ChampTransactionException ex) {
421
422       throw new CrudException(ex.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
423     }
424   }
425
426   @Override
427   public void deleteVertex(String id, String type) throws CrudException {
428
429     try {
430
431       // First, retrieve the vertex that we intend to delete.
432       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
433
434       // Did we find it?
435       if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
436         throw new CrudException("Failed to delete vertex with id: " + id + " - vertex does not exist.",
437             javax.ws.rs.core.Response.Status.NOT_FOUND);
438       }
439
440       // Now, verify that there are no edges incident to the vertex (they must
441       // be deleted
442       // first if so).
443       Stream<ChampRelationship> relationships = champApi.retrieveRelationships(retrievedVertex.get());
444
445       if (relationships.count() > 0) {
446         throw new CrudException("Attempt to delete vertex with id " + id + " which has incident edges.",
447             javax.ws.rs.core.Response.Status.BAD_REQUEST);
448       }
449
450       // Finally, we can attempt to delete our vertex.
451       champApi.deleteObject(Long.parseLong(id));
452
453     } catch (NumberFormatException | ChampUnmarshallingException | ChampObjectNotExistsException
454         | ChampTransactionException e) {
455
456       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
457     }
458   }
459
460   @Override
461   public void deleteEdge(String id, String type) throws CrudException {
462
463     try {
464
465       // First, retrieve the edge that we want to delete.
466       Optional<ChampRelationship> relationshipToDelete = champApi.retrieveRelationship(getRelKey(id));
467
468       // Did we find it?
469       if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
470         throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
471             javax.ws.rs.core.Response.Status.NOT_FOUND);
472       }
473
474       // Now we can delete the edge.
475       champApi.deleteRelationship(relationshipToDelete.get());
476
477     } catch (ChampRelationshipNotExistsException | NumberFormatException | ChampUnmarshallingException
478         | ChampTransactionException e) {
479
480       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
481     }
482   }
483
484   /**
485    * This helper method generates a string representation of a properties map
486    * for logging purposes.
487    *
488    * @param properties
489    *          - The properties map to be converted.
490    * @return - The log statement friendly conversion of the properties map.
491    */
492   private String propertiesMapToString(Map<String, Object> properties) {
493
494     StringBuilder sb = new StringBuilder();
495     sb.append("{");
496
497     for (String key : properties.keySet()) {
498       sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") ");
499     }
500
501     sb.append("}");
502
503     return sb.toString();
504   }
505
506   /**
507    * This helper method constructs a {@link ChampObject} suitable for passing to
508    * the Champ library.
509    *
510    * @param type
511    *          - The type to assign to our ChampObject
512    * @param properties
513    *          - The set of properties to assign to our ChampObject
514    * @return - A populated ChampObject
515    */
516   private ChampObject buildChampObject(String type, Map<String, Object> properties) {
517
518     ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create().ofType(type).withoutKey();
519
520     for (String key : properties.keySet()) {
521       objectInProgress.withProperty(key, properties.get(key));
522     }
523     return objectInProgress.build();
524   }
525
526   /**
527    * This helper method constructs a {@link ChampObject} suitable for passing to
528    * the Champ library.
529    *
530    * @param id
531    *          - Unique identifier for this object.
532    * @param type
533    *          - The type to assign to our ChampObject
534    * @param properties
535    *          - The set of properties to assign to our ChampObject
536    * @return - A populated ChampObject
537    */
538   private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
539
540     ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create().ofType(type).withKey(Long.parseLong(id));
541
542     for (String key : properties.keySet()) {
543       objectInProgress.withProperty(key, properties.get(key));
544     }
545     return objectInProgress.build();
546   }
547
548   private Vertex vertexFromChampObject(ChampObject champObject, String type) {
549
550     // Get the identifier for this vertex from the Champ object.
551     Object id = champObject.getKey().orElse("");
552
553     // Start building our {@link Vertex} object.
554     Vertex.Builder vertexBuilder = new Vertex.Builder(type);
555     vertexBuilder.id(id.toString());
556
557     // Convert the properties associated with the Champ object into the form
558     // expected for
559     // a Vertex object.
560     for (String key : champObject.getProperties().keySet()) {
561       vertexBuilder.property(key, champObject.getProperties().get(key));
562     }
563
564     // ...and return it.
565     return vertexBuilder.build();
566   }
567
568   /**
569    * This helper method converts a {@link ChampRelationship} from the Champ
570    * library into an equivalent {@link Edge} object that is understood by the
571    * CRUD Service.
572    *
573    * @param relationship
574    *          - The ChampRelationship object to be converted.
575    * @return - An Edge object corresponding to the supplied ChampRelationship
576    */
577   private Edge edgeFromChampRelationship(ChampRelationship relationship) {
578
579     // Populate the edge's id, if available.
580     Object relationshipId = relationship.getKey().orElse("");
581
582     Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType()).id(relationshipId.toString());
583     edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
584         relationship.getSource().getProperties()
585             .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
586                 ? relationship.getSource().getType()
587                 : relationship.getSource().getProperties()
588                     .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
589     edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
590         relationship.getTarget().getProperties()
591             .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
592                 ? relationship.getTarget().getType()
593                 : relationship.getTarget().getProperties()
594                     .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
595
596     for (String key : relationship.getProperties().keySet()) {
597       edgeBuilder.property(key, relationship.getProperties().get(key).toString());
598     }
599
600     return edgeBuilder.build();
601   }
602
603   /**
604    * Performs any necessary shut down operations when the DAO is no longer
605    * needed.
606    */
607   public void close() {
608
609     if (champApi != null) {
610
611       logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);
612
613       champApi.shutdown();
614     }
615   }
616
617   @Override
618   public String openTransaction() {
619
620     ChampTransaction transaction = champApi.openTransaction();
621
622     transactions.put(transaction.id(), transaction);
623     logger.info(CrudServiceMsgs.TRANSACTION, "Stored transaction " + transaction.id() + " in hashmap");
624     logger.info(CrudServiceMsgs.TRANSACTION, "Hash map contents:");
625     for (String key : transactions.keySet()) {
626       logger.info(CrudServiceMsgs.TRANSACTION, key);
627     }
628     return transaction.id();
629   }
630
631   @Override
632   public void commitTransaction(String id) throws CrudException {
633
634     try {
635       champApi.commitTransaction(getTransaction(id));
636     } catch (ChampTransactionException e) {
637       throw new CrudException("Error while commiting transaction " + id,
638           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
639     }
640     transactions.remove(id);
641   }
642
643   @Override
644   public void rollbackTransaction(String id) throws CrudException {
645
646     try {
647       champApi.rollbackTransaction(getTransaction(id));
648     } catch (ChampTransactionException e) {
649       throw new CrudException("Error while transaction rollback " + id,
650           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
651     }
652     transactions.remove(id);
653   }
654
655   private ChampTransaction getTransaction(String id) throws CrudException {
656
657     logger.info(CrudServiceMsgs.TRANSACTION, "Looking up transaction " + id);
658     if (transactions.containsKey(id)) {
659       logger.info(CrudServiceMsgs.TRANSACTION, "Found it!");
660       return (transactions.get(id));
661     } else {
662       logger.info(CrudServiceMsgs.TRANSACTION, "Didn't find transaction id " + id + ".  Hash map contains: ");
663       for (String key : transactions.keySet()) {
664         logger.info(CrudServiceMsgs.TRANSACTION, key);
665       }
666       throw new CrudException("No open transaction with id: " + id, javax.ws.rs.core.Response.Status.NOT_FOUND);
667     }
668   }
669
670   @Override
671   public Vertex addVertex(String type, Map<String, Object> properties, String txId) throws CrudException {
672     if (logger.isDebugEnabled()) {
673       logger.debug("Add/update vertex: {label: " + type + " properties:" + propertiesMapToString(properties));
674     }
675
676     // Add the aai_node_type so that AAI can read the data created by gizmo
677     properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
678
679     // Create an object to represent our vertex in the format expected by the
680     // Champ library.
681     ChampObject objectToCreate = buildChampObject(type, properties);
682
683     try {
684
685       // Ask the Champ library to store our vertex, placing the returned object
686       // into a
687       // list so that we can easily put that into our result object.
688       return vertexFromChampObject(champApi.storeObject(objectToCreate, Optional.of(getTransaction(txId))), type);
689
690     } catch (ChampMarshallingException | ChampSchemaViolationException | ChampObjectNotExistsException
691         | ChampTransactionException e) {
692
693       // Something went wrong - throw an exception.
694       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
695     }
696   }
697
698   @Override
699   public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String txId)
700       throws CrudException {
701     // For now, assume source and target are straight ids...
702     try {
703
704       Optional<ChampObject> sourceObject = champApi.retrieveObject(Long.parseLong(source.getId().get()),
705           Optional.of(getTransaction(txId)));
706       if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
707         throw new CrudException(
708             "Error creating edge - source vertex with id " + source + " does not exist in graph data base",
709             javax.ws.rs.core.Response.Status.BAD_REQUEST);
710       }
711
712       Optional<ChampObject> targetObject = champApi.retrieveObject(Long.parseLong(target.getId().get()),
713           Optional.of(getTransaction(txId)));
714       if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
715         throw new CrudException(
716             "Error creating edge - target vertex with id " + target + " does not exist in graph data base",
717             javax.ws.rs.core.Response.Status.BAD_REQUEST);
718       }
719
720       // Now, create the ChampRelationship object for our edge and store it in
721       // the graph database.
722       return edgeFromChampRelationship(champApi.storeRelationship(
723           new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type).properties(properties).build(),
724           Optional.of(getTransaction(txId))));
725
726     } catch (ChampMarshallingException | ChampObjectNotExistsException | ChampSchemaViolationException
727         | ChampTransactionException | ChampRelationshipNotExistsException | ChampUnmarshallingException e) {
728
729       throw new CrudException("Error creating edge: " + e.getMessage(),
730           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
731     }
732
733   }
734
735   @Override
736   public Vertex updateVertex(String id, String type, Map<String, Object> properties, String txId) throws CrudException {
737     if (logger.isDebugEnabled()) {
738       logger.debug("Update vertex with id: " + id + " with properties: " + propertiesMapToString(properties));
739     }
740     // Add the aai_node_type so that AAI can read the data created by gizmo
741     properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
742
743     try {
744       // Now, build the updated version of the Champ Object...
745       ChampObject updateObject = buildChampObject(id, type, properties);
746       // ...and send it to the Champ library.
747       return vertexFromChampObject(champApi.replaceObject(updateObject, Optional.of(getTransaction(txId))), type);
748
749     } catch (ChampObjectNotExistsException e) {
750       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
751     } catch (NumberFormatException | ChampMarshallingException | ChampTransactionException
752         | ChampSchemaViolationException e) {
753       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
754     }
755   }
756
757   @Override
758   public boolean transactionExists(String id) throws CrudException {
759     return transactions.containsKey(id);
760   }
761
762   @Override
763   public void deleteVertex(String id, String type, String txId) throws CrudException {
764     try {
765
766       // First, retrieve the vertex that we intend to delete.
767       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id),
768           Optional.of(getTransaction(txId)));
769
770       // Did we find it?
771       if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
772         throw new CrudException("Failed to delete vertex with id: " + id + " - vertex does not exist.",
773             javax.ws.rs.core.Response.Status.NOT_FOUND);
774       }
775
776       // Now, verify that there are no edges incident to the vertex (they must
777       // be deleted
778       // first if so).
779       Stream<ChampRelationship> relationships = champApi.retrieveRelationships(retrievedVertex.get(),
780           Optional.of(getTransaction(txId)));
781
782       if (relationships.count() > 0) {
783         throw new CrudException("Attempt to delete vertex with id " + id + " which has incident edges.",
784             javax.ws.rs.core.Response.Status.BAD_REQUEST);
785       }
786
787       // Finally, we can attempt to delete our vertex.
788       champApi.deleteObject(Long.parseLong(id), Optional.of(getTransaction(txId)));
789
790     } catch (NumberFormatException | ChampUnmarshallingException | ChampObjectNotExistsException
791         | ChampTransactionException e) {
792
793       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
794     }
795
796   }
797
798   @Override
799   public Edge updateEdge(Edge edge, String txId) throws CrudException {
800     if (logger.isDebugEnabled()) {
801       logger.debug(
802           "Update edge with id: " + edge.getId() + " with properties: " + propertiesMapToString(edge.getProperties()));
803     }
804
805     try {
806       // Now, build the updated version of the Champ Relationship...
807       ChampRelationship updateRelationship = new ChampRelationship.Builder(
808           buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
809               edge.getSource().getProperties()),
810           buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
811               edge.getTarget().getProperties()),
812           edge.getType()).key(getRelKey(edge.getId().get())).properties(edge.getProperties()).build();
813       // ...and send it to the Champ library.
814       return edgeFromChampRelationship(
815           champApi.replaceRelationship(updateRelationship, Optional.of(getTransaction(txId))));
816
817     } catch (ChampRelationshipNotExistsException ex) {
818       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
819     } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException
820         | ChampSchemaViolationException | ChampTransactionException ex) {
821
822       throw new CrudException(ex.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
823     }
824   }
825
826   @Override
827   public void deleteEdge(String id, String type, String txId) throws CrudException {
828     try {
829
830       // First, retrieve the edge that we want to delete.
831       Optional<ChampRelationship> relationshipToDelete = champApi.retrieveRelationship(getRelKey(id),
832           Optional.of(getTransaction(txId)));
833
834       // Did we find it?
835       if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
836         throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
837             javax.ws.rs.core.Response.Status.NOT_FOUND);
838       }
839
840       // Now we can delete the edge.
841       champApi.deleteRelationship(relationshipToDelete.get(), Optional.of(getTransaction(txId)));
842
843     } catch (ChampRelationshipNotExistsException | NumberFormatException | ChampUnmarshallingException
844         | ChampTransactionException e) {
845
846       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
847     }
848
849   }
850
851   @Override
852   public Edge getEdge(String id, String type, String txId) throws CrudException {
853     if (logger.isDebugEnabled()) {
854       logger.debug("Get edge with id: " + id);
855     }
856
857     try {
858
859       // Request the edge from the graph db.
860       Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id),
861           Optional.of(getTransaction(txId)));
862
863       // Did we find it?
864       if (relationship.isPresent() && relationship.get().getType().equals(type)) {
865
866         // Yup - return the result.
867         return edgeFromChampRelationship(relationship.get());
868
869       } else {
870
871         // We didn't find an edge with the supplied id, so throw an exception.
872         throw new CrudException("No edge with id " + id + " found in graph",
873             javax.ws.rs.core.Response.Status.NOT_FOUND);
874       }
875
876     } catch (ChampUnmarshallingException | ChampTransactionException e) {
877
878       // Something went wrong, so throw an exception.
879       throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
880     }
881   }
882
883 }