2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
24 package org.openecomp.crud.dao.champ;
26 import org.openecomp.aai.champcore.ChampGraph;
27 import org.openecomp.aai.champcore.exceptions.ChampMarshallingException;
28 import org.openecomp.aai.champcore.exceptions.ChampObjectNotExistsException;
29 import org.openecomp.aai.champcore.exceptions.ChampRelationshipNotExistsException;
30 import org.openecomp.aai.champcore.exceptions.ChampSchemaViolationException;
31 import org.openecomp.aai.champcore.exceptions.ChampTransactionException;
32 import org.openecomp.aai.champcore.exceptions.ChampUnmarshallingException;
33 import org.openecomp.aai.champcore.model.ChampObject;
34 import org.openecomp.aai.champcore.model.ChampRelationship;
35 import org.openecomp.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep;
36 import org.openecomp.cl.api.Logger;
37 import org.openecomp.cl.eelf.LoggerFactory;
38 import org.openecomp.crud.dao.GraphDao;
39 import org.openecomp.crud.entity.Edge;
40 import org.openecomp.crud.entity.Vertex;
41 import org.openecomp.crud.exception.CrudException;
42 import org.openecomp.crud.logging.CrudServiceMsgs;
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.List;
48 import java.util.Optional;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
54 * This is the integration layer between the CRUD API service and the low level Champ library
55 * for graph database interaction.
57 public class ChampDao implements GraphDao {
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";
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";
73 public static final String DEFAULT_GRAPH_NAME = "default_graph";
75 private enum GraphType {
82 * Instance of the API used for interacting with the Champ library.
84 private ChampGraph champApi = null;
86 private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName());
90 * Creates a new instance of the ChampDao.
92 * @param champGraph - Concrete implementation of the graph dao layer
94 public ChampDao(ChampGraph champGraph) {
95 this.champApi = champGraph;
99 public Vertex getVertex(String id) throws CrudException {
103 if (logger.isDebugEnabled()) {
104 logger.debug("getVertex with id: " + id);
107 long idAsLong = Long.parseLong(id);
109 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
111 String nodeType = org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName();
112 if(retrievedVertex.isPresent() &&
113 retrievedVertex.get().getProperties().get(nodeType)!=null) {
114 return vertexFromChampObject(retrievedVertex.get(),
115 retrievedVertex.get().getProperties().get(nodeType).toString());
118 // We didn't find a vertex with the supplied id, so just throw an
120 throw new CrudException("No vertex with id " + id + " found in graph",
121 javax.ws.rs.core.Response.Status.NOT_FOUND);
124 } catch (ChampUnmarshallingException | ChampTransactionException e) {
126 // Something went wrong - throw an exception.
127 throw new CrudException(e.getMessage(),
128 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
133 public Vertex getVertex(String id, String type) throws CrudException {
137 if (logger.isDebugEnabled()) {
138 logger.debug("getVertex with id: " + id);
141 long idAsLong = Long.parseLong(id);
143 // Request the vertex from the graph db.
144 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
147 if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties()
148 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties()
149 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
150 .equalsIgnoreCase(type)) {
152 // Yup, convert it to a Vector object and return it.
153 return vertexFromChampObject(retrievedVertex.get(),type);
157 // We didn't find a vertex with the supplied id, so just throw an
159 throw new CrudException("No vertex with id " + id + " found in graph",
160 javax.ws.rs.core.Response.Status.NOT_FOUND);
163 } catch (ChampUnmarshallingException | ChampTransactionException e) {
165 // Something went wrong - throw an exception.
166 throw new CrudException(e.getMessage(),
167 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
173 public List<Edge> getVertexEdges(String id) throws CrudException {
175 if (logger.isDebugEnabled()) {
176 logger.debug("get Edges incident to vertex with id: " + id + " from graph");
180 long idAsLong = Long.parseLong(id); // GDF - what to do about id???
182 // Request the vertex from the graph db.
183 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
186 if (retrievedVertex.isPresent()) {
188 // Query the Champ library for the edges which are incident to the specified
190 Stream<ChampRelationship> relationships =
191 champApi.retrieveRelationships(retrievedVertex.get());
193 // Build an edge list from the result stream.
194 List<Edge> edges = new ArrayList<Edge>();
195 relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
201 // We couldn't find the specified vertex, so throw an exception.
202 throw new CrudException("No vertex with id " + id + " found in graph",
203 javax.ws.rs.core.Response.Status.NOT_FOUND);
206 } catch (ChampUnmarshallingException e) {
208 // Something went wrong, so throw an exception.
209 throw new CrudException(e.getMessage(),
210 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
212 } catch (ChampObjectNotExistsException e) {
214 // We couldn't find the specified vertex, so throw an exception.
215 throw new CrudException("No vertex with id " + id + " found in graph",
216 javax.ws.rs.core.Response.Status.NOT_FOUND);
217 } catch (ChampTransactionException e) {
218 throw new CrudException("Transaction error occured",
219 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
225 public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
227 if (logger.isDebugEnabled()) {
228 logger.debug("Add/update vertex: {label: " + type
229 + " properties:" + propertiesMapToString(properties));
232 //Add the aai_node_type so that AAI can read the data created by gizmo
233 properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
235 // Create an object to represent our vertex in the format expected by the Champ library.
236 ChampObject objectToCreate = buildChampObject(type, properties);
240 // Ask the Champ library to store our vertex, placing the returned object into a
241 // list so that we can easily put that into our result object.
242 return vertexFromChampObject(champApi.storeObject(objectToCreate),type);
244 } catch (ChampMarshallingException
245 | ChampSchemaViolationException
246 | ChampObjectNotExistsException
247 | ChampTransactionException e) {
249 // Something went wrong - throw an exception.
250 throw new CrudException(e.getMessage(),
251 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
257 public Vertex updateVertex(String id, String type, Map<String, Object> properties)
258 throws CrudException {
260 if (logger.isDebugEnabled()) {
261 logger.debug("Update vertex with id: " + id + " with properties: "
262 + propertiesMapToString(properties));
264 //Add the aai_node_type so that AAI can read the data created by gizmo
265 properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
268 // Now, build the updated version of the Champ Object...
269 ChampObject updateObject = buildChampObject(id, type, properties);
270 // ...and send it to the Champ library.
271 return vertexFromChampObject(champApi.replaceObject(updateObject),type);
273 } catch (ChampObjectNotExistsException e) {
274 throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
275 } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
276 throw new CrudException(e.getMessage(),
277 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
278 } catch (ChampTransactionException e) {
279 throw new CrudException("Transaction error occured",
280 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
287 public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
289 if (logger.isDebugEnabled()) {
290 logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
291 + propertiesMapToString(filter));
295 filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
298 Stream<ChampObject> retrievedVertices;
300 retrievedVertices = champApi.queryObjects(filter);
302 } catch (ChampTransactionException e) {
303 throw new CrudException("Transaction error occured",
304 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
307 List<Vertex> vertices = retrievedVertices
308 .map(v -> vertexFromChampObject(v,type))
309 .collect(Collectors.toList());
312 if (logger.isDebugEnabled()) {
313 logger.debug("Resulting vertex list: " + retrievedVertices);
316 // ...and return it to the caller.
320 private Object getRelKey(String id) {
322 // convert into Long if applicable . TODO : revisit in story NUC-304
324 key = Long.parseLong(id);
325 } catch (NumberFormatException e) {
326 // The id isn't a Long, leave it as a string
333 public Edge getEdge(String id, String type) throws CrudException {
335 if (logger.isDebugEnabled()) {
336 logger.debug("Get edge with id: " + id);
341 // Request the edge from the graph db.
342 Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
345 if (relationship.isPresent() && relationship.get().getType().equals(type)) {
347 // Yup - return the result.
348 return edgeFromChampRelationship(relationship.get());
352 // We didn't find an edge with the supplied id, so throw an exception.
353 throw new CrudException("No edge with id " + id + " found in graph",
354 javax.ws.rs.core.Response.Status.NOT_FOUND);
357 } catch (ChampUnmarshallingException | ChampTransactionException e) {
359 // Something went wrong, so throw an exception.
360 throw new CrudException(e.getMessage(),
361 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
366 public Edge addEdge(String type,
369 Map<String, Object> properties) throws CrudException {
371 // For now, assume source and target are straight ids...
374 Optional<ChampObject> sourceObject
375 = champApi.retrieveObject(Long.parseLong(source.getId().get()));
376 if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
377 throw new CrudException("Error creating edge - source vertex with id " + source
378 + " does not exist in graph data base",
379 javax.ws.rs.core.Response.Status.BAD_REQUEST);
382 Optional<ChampObject> targetObject
383 = champApi.retrieveObject(Long.parseLong(target.getId().get()));
384 if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
385 throw new CrudException("Error creating edge - target vertex with id " + target
386 + " does not exist in graph data base",
387 javax.ws.rs.core.Response.Status.BAD_REQUEST);
390 // Now, create the ChampRelationship object for our edge and store it in
391 // the graph database.
392 return edgeFromChampRelationship(
393 champApi.storeRelationship(
394 new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type)
395 .properties(properties)
398 } catch (ChampMarshallingException
399 | ChampObjectNotExistsException
400 | ChampSchemaViolationException
401 | ChampRelationshipNotExistsException
402 | ChampUnmarshallingException | NumberFormatException | ChampTransactionException e) {
404 throw new CrudException("Error creating edge: " + e.getMessage(),
405 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
411 public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
413 filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
415 Stream<ChampRelationship> retrievedRelationships;
417 retrievedRelationships = champApi.queryRelationships(filter);
419 } catch (ChampTransactionException e) {
420 throw new CrudException("Transaction error occured",
421 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
424 // Process the result stream from the Champ library into an Edge list, keeping only
425 // edges of the specified type.
426 List<Edge> edges = retrievedRelationships
427 .map(r -> edgeFromChampRelationship(r))
428 .collect(Collectors.toList());
434 public Edge updateEdge(Edge edge) throws CrudException {
436 if (logger.isDebugEnabled()) {
437 logger.debug("Update edge with id: " + edge.getId() + " with properties: "
438 + propertiesMapToString(edge.getProperties()));
442 // Now, build the updated version of the Champ Relationship...
443 ChampRelationship updateRelationship = new ChampRelationship.Builder(
444 buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
445 edge.getSource().getProperties()),
446 buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
447 edge.getTarget().getProperties()),
448 edge.getType()).key(getRelKey(edge.getId().get()))
449 .properties(edge.getProperties()).build();
450 // ...and send it to the Champ library.
451 return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
454 } catch (ChampRelationshipNotExistsException ex) {
455 throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
456 } catch (NumberFormatException |
457 ChampUnmarshallingException |
458 ChampMarshallingException |
459 ChampSchemaViolationException |
460 ChampTransactionException ex) {
462 throw new CrudException(ex.getMessage(),
463 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
468 public void deleteVertex(String id, String type) throws CrudException {
472 // First, retrieve the vertex that we intend to delete.
473 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
476 if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
477 throw new CrudException("Failed to delete vertex with id: "
478 + id + " - vertex does not exist.",
479 javax.ws.rs.core.Response.Status.NOT_FOUND);
482 // Now, verify that there are no edges incident to the vertex (they must be deleted
484 Stream<ChampRelationship> relationships =
485 champApi.retrieveRelationships(retrievedVertex.get());
487 if (relationships.count() > 0) {
488 throw new CrudException("Attempt to delete vertex with id "
489 + id + " which has incident edges.",
490 javax.ws.rs.core.Response.Status.BAD_REQUEST);
493 // Finally, we can attempt to delete our vertex.
494 champApi.deleteObject(Long.parseLong(id));
497 } catch (NumberFormatException
498 | ChampUnmarshallingException
499 | ChampObjectNotExistsException
500 | ChampTransactionException e) {
502 throw new CrudException(e.getMessage(),
503 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
508 public void deleteEdge(String id, String type) throws CrudException {
512 // First, retrieve the edge that we want to delete.
513 Optional<ChampRelationship> relationshipToDelete
514 = champApi.retrieveRelationship(getRelKey(id));
518 if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
519 throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
520 javax.ws.rs.core.Response.Status.NOT_FOUND);
523 // Now we can delete the edge.
524 champApi.deleteRelationship(relationshipToDelete.get());
526 } catch (ChampRelationshipNotExistsException
527 | NumberFormatException
528 | ChampUnmarshallingException
529 | ChampTransactionException e) {
531 throw new CrudException(e.getMessage(),
532 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
538 * This helper method generates a string representation of a properties map for
541 * @param properties - The properties map to be converted.
542 * @return - The log statement friendly conversion of the properties map.
544 private String propertiesMapToString(Map<String, Object> properties) {
546 StringBuilder sb = new StringBuilder();
549 for (String key : properties.keySet()) {
550 sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") ");
555 return sb.toString();
560 * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
562 * @param type - The type to assign to our ChampObject
563 * @param properties - The set of properties to assign to our ChampObject
564 * @return - A populated ChampObject
566 private ChampObject buildChampObject(String type, Map<String, Object> properties) {
568 ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
572 for (String key : properties.keySet()) {
573 objectInProgress.withProperty(key, properties.get(key));
575 return objectInProgress.build();
580 * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
582 * @param id - Unique identifier for this object.
583 * @param type - The type to assign to our ChampObject
584 * @param properties - The set of properties to assign to our ChampObject
585 * @return - A populated ChampObject
587 private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
589 ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
591 .withKey(Long.parseLong(id));
593 for (String key : properties.keySet()) {
594 objectInProgress.withProperty(key, properties.get(key));
596 return objectInProgress.build();
602 private Vertex vertexFromChampObject(ChampObject champObject, String type) {
604 // Get the identifier for this vertex from the Champ object.
605 Object id = champObject.getKey().orElse("");
607 // Start building our {@link Vertex} object.
608 Vertex.Builder vertexBuilder = new Vertex.Builder(type);
609 vertexBuilder.id(id.toString());
611 // Convert the properties associated with the Champ object into the form expected for
613 for (String key : champObject.getProperties().keySet()) {
614 vertexBuilder.property(key, champObject.getProperties().get(key));
618 return vertexBuilder.build();
623 * This helper method converts a {@link ChampRelationship} from the Champ library into an
624 * equivalent {@link Edge} object that is understood by the CRUD Service.
626 * @param relationship - The ChampRelationship object to be converted.
627 * @return - An Edge object corresponding to the supplied ChampRelationship
629 private Edge edgeFromChampRelationship(ChampRelationship relationship) {
631 // Populate the edge's id, if available.
632 Object relationshipId = relationship.getKey().orElse("");
634 Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType())
635 .id(relationshipId.toString());
636 edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
637 relationship.getSource().getProperties()
638 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
639 ? relationship.getSource().getType()
640 : relationship.getSource().getProperties()
641 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
642 edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
643 relationship.getTarget().getProperties()
644 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
645 ? relationship.getTarget().getType()
646 : relationship.getTarget().getProperties()
647 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
649 for (String key : relationship.getProperties().keySet()) {
650 edgeBuilder.property(key, relationship.getProperties().get(key).toString());
653 return edgeBuilder.build();
657 * Performs any necessary shut down operations when the DAO is no longer needed.
659 public void close() {
661 if (champApi != null) {
663 logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);