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, String type) throws CrudException {
103 if (logger.isDebugEnabled()) {
104 logger.debug("getVertex with id: " + id);
107 long idAsLong = Long.parseLong(id);
109 // Request the vertex from the graph db.
110 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
113 if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties()
114 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties()
115 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
116 .equalsIgnoreCase(type)) {
118 // Yup, convert it to a Vector object and return it.
119 return vertexFromChampObject(retrievedVertex.get(),type);
123 // We didn't find a vertex with the supplied id, so just throw an
125 throw new CrudException("No vertex with id " + id + " found in graph",
126 javax.ws.rs.core.Response.Status.NOT_FOUND);
129 } catch (ChampUnmarshallingException | ChampTransactionException e) {
131 // Something went wrong - throw an exception.
132 throw new CrudException(e.getMessage(),
133 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
139 public List<Edge> getVertexEdges(String id) throws CrudException {
141 if (logger.isDebugEnabled()) {
142 logger.debug("get Edges incident to vertex with id: " + id + " from graph");
146 long idAsLong = Long.parseLong(id); // GDF - what to do about id???
148 // Request the vertex from the graph db.
149 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
152 if (retrievedVertex.isPresent()) {
154 // Query the Champ library for the edges which are incident to the specified
156 Stream<ChampRelationship> relationships =
157 champApi.retrieveRelationships(retrievedVertex.get());
159 // Build an edge list from the result stream.
160 List<Edge> edges = new ArrayList<Edge>();
161 relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
167 // We couldn't find the specified vertex, so throw an exception.
168 throw new CrudException("No vertex with id " + id + " found in graph",
169 javax.ws.rs.core.Response.Status.NOT_FOUND);
172 } catch (ChampUnmarshallingException e) {
174 // Something went wrong, so throw an exception.
175 throw new CrudException(e.getMessage(),
176 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
178 } catch (ChampObjectNotExistsException e) {
180 // We couldn't find the specified vertex, so throw an exception.
181 throw new CrudException("No vertex with id " + id + " found in graph",
182 javax.ws.rs.core.Response.Status.NOT_FOUND);
183 } catch (ChampTransactionException e) {
184 throw new CrudException("Transaction error occured",
185 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
191 public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
193 if (logger.isDebugEnabled()) {
194 logger.debug("Add/update vertex: {label: " + type
195 + " properties:" + propertiesMapToString(properties));
198 //Add the aai_node_type so that AAI can read the data created by gizmo
199 properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
201 // Create an object to represent our vertex in the format expected by the Champ library.
202 ChampObject objectToCreate = buildChampObject(type, properties);
206 // Ask the Champ library to store our vertex, placing the returned object into a
207 // list so that we can easily put that into our result object.
208 return vertexFromChampObject(champApi.storeObject(objectToCreate),type);
210 } catch (ChampMarshallingException
211 | ChampSchemaViolationException
212 | ChampObjectNotExistsException
213 | ChampTransactionException e) {
215 // Something went wrong - throw an exception.
216 throw new CrudException(e.getMessage(),
217 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
223 public Vertex updateVertex(String id, String type, Map<String, Object> properties)
224 throws CrudException {
226 if (logger.isDebugEnabled()) {
227 logger.debug("Update vertex with id: " + id + " with properties: "
228 + propertiesMapToString(properties));
230 //Add the aai_node_type so that AAI can read the data created by gizmo
231 properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
234 // Now, build the updated version of the Champ Object...
235 ChampObject updateObject = buildChampObject(id, type, properties);
236 // ...and send it to the Champ library.
237 return vertexFromChampObject(champApi.replaceObject(updateObject),type);
239 } catch (ChampObjectNotExistsException e) {
240 throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
241 } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
242 throw new CrudException(e.getMessage(),
243 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
244 } catch (ChampTransactionException e) {
245 throw new CrudException("Transaction error occured",
246 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
253 public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
255 if (logger.isDebugEnabled()) {
256 logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
257 + propertiesMapToString(filter));
261 filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
264 Stream<ChampObject> retrievedVertices;
266 retrievedVertices = champApi.queryObjects(filter);
268 } catch (ChampTransactionException e) {
269 throw new CrudException("Transaction error occured",
270 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
273 List<Vertex> vertices = retrievedVertices
274 .map(v -> vertexFromChampObject(v,type))
275 .collect(Collectors.toList());
278 if (logger.isDebugEnabled()) {
279 logger.debug("Resulting vertex list: " + retrievedVertices);
282 // ...and return it to the caller.
286 private Object getRelKey(String id) {
288 // convert into Long if applicable . TODO : revisit in story NUC-304
290 key = Long.parseLong(id);
291 } catch (NumberFormatException e) {
292 // The id isn't a Long, leave it as a string
299 public Edge getEdge(String id, String type) throws CrudException {
301 if (logger.isDebugEnabled()) {
302 logger.debug("Get edge with id: " + id);
307 // Request the edge from the graph db.
308 Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
311 if (relationship.isPresent() && relationship.get().getType().equals(type)) {
313 // Yup - return the result.
314 return edgeFromChampRelationship(relationship.get());
318 // We didn't find an edge with the supplied id, so throw an exception.
319 throw new CrudException("No edge with id " + id + " found in graph",
320 javax.ws.rs.core.Response.Status.NOT_FOUND);
323 } catch (ChampUnmarshallingException | ChampTransactionException e) {
325 // Something went wrong, so throw an exception.
326 throw new CrudException(e.getMessage(),
327 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
332 public Edge addEdge(String type,
335 Map<String, Object> properties) throws CrudException {
337 // For now, assume source and target are straight ids...
340 Optional<ChampObject> sourceObject
341 = champApi.retrieveObject(Long.parseLong(source.getId().get()));
342 if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
343 throw new CrudException("Error creating edge - source vertex with id " + source
344 + " does not exist in graph data base",
345 javax.ws.rs.core.Response.Status.BAD_REQUEST);
348 Optional<ChampObject> targetObject
349 = champApi.retrieveObject(Long.parseLong(target.getId().get()));
350 if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
351 throw new CrudException("Error creating edge - target vertex with id " + target
352 + " does not exist in graph data base",
353 javax.ws.rs.core.Response.Status.BAD_REQUEST);
356 // Now, create the ChampRelationship object for our edge and store it in
357 // the graph database.
358 return edgeFromChampRelationship(
359 champApi.storeRelationship(
360 new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type)
361 .properties(properties)
364 } catch (ChampMarshallingException
365 | ChampObjectNotExistsException
366 | ChampSchemaViolationException
367 | ChampRelationshipNotExistsException
368 | ChampUnmarshallingException | NumberFormatException | ChampTransactionException e) {
370 throw new CrudException("Error creating edge: " + e.getMessage(),
371 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
377 public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
379 filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
381 Stream<ChampRelationship> retrievedRelationships;
383 retrievedRelationships = champApi.queryRelationships(filter);
385 } catch (ChampTransactionException e) {
386 throw new CrudException("Transaction error occured",
387 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
390 // Process the result stream from the Champ library into an Edge list, keeping only
391 // edges of the specified type.
392 List<Edge> edges = retrievedRelationships
393 .map(r -> edgeFromChampRelationship(r))
394 .collect(Collectors.toList());
400 public Edge updateEdge(Edge edge) throws CrudException {
402 if (logger.isDebugEnabled()) {
403 logger.debug("Update edge with id: " + edge.getId() + " with properties: "
404 + propertiesMapToString(edge.getProperties()));
408 // Now, build the updated version of the Champ Relationship...
409 ChampRelationship updateRelationship = new ChampRelationship.Builder(
410 buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
411 edge.getSource().getProperties()),
412 buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
413 edge.getTarget().getProperties()),
414 edge.getType()).key(getRelKey(edge.getId().get()))
415 .properties(edge.getProperties()).build();
416 // ...and send it to the Champ library.
417 return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
420 } catch (ChampRelationshipNotExistsException ex) {
421 throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
422 } catch (NumberFormatException |
423 ChampUnmarshallingException |
424 ChampMarshallingException |
425 ChampSchemaViolationException |
426 ChampTransactionException ex) {
428 throw new CrudException(ex.getMessage(),
429 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
434 public void deleteVertex(String id, String type) throws CrudException {
438 // First, retrieve the vertex that we intend to delete.
439 Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
442 if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
443 throw new CrudException("Failed to delete vertex with id: "
444 + id + " - vertex does not exist.",
445 javax.ws.rs.core.Response.Status.NOT_FOUND);
448 // Now, verify that there are no edges incident to the vertex (they must be deleted
450 Stream<ChampRelationship> relationships =
451 champApi.retrieveRelationships(retrievedVertex.get());
453 if (relationships.count() > 0) {
454 throw new CrudException("Attempt to delete vertex with id "
455 + id + " which has incident edges.",
456 javax.ws.rs.core.Response.Status.BAD_REQUEST);
459 // Finally, we can attempt to delete our vertex.
460 champApi.deleteObject(Long.parseLong(id));
463 } catch (NumberFormatException
464 | ChampUnmarshallingException
465 | ChampObjectNotExistsException
466 | ChampTransactionException e) {
468 throw new CrudException(e.getMessage(),
469 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
474 public void deleteEdge(String id, String type) throws CrudException {
478 // First, retrieve the edge that we want to delete.
479 Optional<ChampRelationship> relationshipToDelete
480 = champApi.retrieveRelationship(getRelKey(id));
484 if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
485 throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
486 javax.ws.rs.core.Response.Status.NOT_FOUND);
489 // Now we can delete the edge.
490 champApi.deleteRelationship(relationshipToDelete.get());
492 } catch (ChampRelationshipNotExistsException
493 | NumberFormatException
494 | ChampUnmarshallingException
495 | ChampTransactionException e) {
497 throw new CrudException(e.getMessage(),
498 javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
504 * This helper method generates a string representation of a properties map for
507 * @param properties - The properties map to be converted.
508 * @return - The log statement friendly conversion of the properties map.
510 private String propertiesMapToString(Map<String, Object> properties) {
512 StringBuilder sb = new StringBuilder();
515 for (String key : properties.keySet()) {
516 sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") ");
521 return sb.toString();
526 * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
528 * @param type - The type to assign to our ChampObject
529 * @param properties - The set of properties to assign to our ChampObject
530 * @return - A populated ChampObject
532 private ChampObject buildChampObject(String type, Map<String, Object> properties) {
534 ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
538 for (String key : properties.keySet()) {
539 objectInProgress.withProperty(key, properties.get(key));
541 return objectInProgress.build();
546 * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
548 * @param id - Unique identifier for this object.
549 * @param type - The type to assign to our ChampObject
550 * @param properties - The set of properties to assign to our ChampObject
551 * @return - A populated ChampObject
553 private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
555 ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
557 .withKey(Long.parseLong(id));
559 for (String key : properties.keySet()) {
560 objectInProgress.withProperty(key, properties.get(key));
562 return objectInProgress.build();
568 private Vertex vertexFromChampObject(ChampObject champObject, String type) {
570 // Get the identifier for this vertex from the Champ object.
571 Object id = champObject.getKey().orElse("");
573 // Start building our {@link Vertex} object.
574 Vertex.Builder vertexBuilder = new Vertex.Builder(type);
575 vertexBuilder.id(id.toString());
577 // Convert the properties associated with the Champ object into the form expected for
579 for (String key : champObject.getProperties().keySet()) {
580 vertexBuilder.property(key, champObject.getProperties().get(key));
584 return vertexBuilder.build();
589 * This helper method converts a {@link ChampRelationship} from the Champ library into an
590 * equivalent {@link Edge} object that is understood by the CRUD Service.
592 * @param relationship - The ChampRelationship object to be converted.
593 * @return - An Edge object corresponding to the supplied ChampRelationship
595 private Edge edgeFromChampRelationship(ChampRelationship relationship) {
597 // Populate the edge's id, if available.
598 Object relationshipId = relationship.getKey().orElse("");
600 Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType())
601 .id(relationshipId.toString());
602 edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
603 relationship.getSource().getProperties()
604 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
605 ? relationship.getSource().getType()
606 : relationship.getSource().getProperties()
607 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
608 edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
609 relationship.getTarget().getProperties()
610 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
611 ? relationship.getTarget().getType()
612 : relationship.getTarget().getProperties()
613 .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
615 for (String key : relationship.getProperties().keySet()) {
616 edgeBuilder.property(key, relationship.getProperties().get(key).toString());
619 return edgeBuilder.build();
623 * Performs any necessary shut down operations when the DAO is no longer needed.
625 public void close() {
627 if (champApi != null) {
629 logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);