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