[AAI-26] Adding gizmo data to the repository.
[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 org.openecomp.aai.champ.ChampAPI;
27 import org.openecomp.aai.champ.ChampGraph;
28 import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
29 import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
30 import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
31 import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
32 import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
33 import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
34 import org.openecomp.aai.champ.model.ChampObject;
35 import org.openecomp.aai.champ.model.ChampRelationship;
36 import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
37 import org.openecomp.cl.api.Logger;
38 import org.openecomp.cl.eelf.LoggerFactory;
39 import org.openecomp.crud.dao.GraphDao;
40 import org.openecomp.crud.entity.Edge;
41 import org.openecomp.crud.entity.Vertex;
42 import org.openecomp.crud.exception.CrudException;
43 import org.openecomp.crud.logging.CrudServiceMsgs;
44
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Optional;
49 import java.util.Properties;
50 import java.util.Random;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
53
54
55 /**
56  * This is the integration layer between the CRUD API service and the low level Champ library
57  * for graph database interaction.
58  */
59 public class ChampDao implements GraphDao {
60
61   public static final String CONFIG_STORAGE_BACKEND = "storage.backend";
62   public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db";
63   public static final String STORAGE_HBASE_DB = "hbase";
64   public static final String STORAGE_CASSANDRA_DB = "cassandra";
65   public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames";
66   public static final String CONFIG_STORAGE_PORT = "storage.port";
67   public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent";
68   public static final String CONFIG_GRAPH_NAME = "graph.name";
69   public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix";
70
71   public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher";
72   public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers";
73
74
75   public static final String DEFAULT_GRAPH_NAME = "default_graph";
76
77   /**
78    * Set of configuration properties for the DAI.
79    */
80   private Properties daoConfig;
81
82   /**
83    * Instance of the API used for interacting with the Champ library.
84    */
85   private ChampGraph champApi = null;
86
87   private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName());
88
89
90   /**
91    * Creates a new instance of the ChampDao.
92    *
93    * @param config - Set of configuration properties to be applied to this instance
94    *               of the DAO.
95    */
96   public ChampDao(Properties config) {
97
98     // Store the configuration properties.
99     daoConfig = config;
100
101     // Apply the configuration to the DAO.
102     configure();
103
104   }
105
106
107   @Override
108   public Vertex getVertex(String id, String type) throws CrudException {
109
110     try {
111
112       if (logger.isDebugEnabled()) {
113         logger.debug("getVertex with id: " + id);
114       }
115
116       long idAsLong = Long.parseLong(id);
117
118       // Request the vertex from the graph db.
119       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
120
121       // Did we find it?
122       if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties()
123           .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties()
124           .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
125           .equalsIgnoreCase(type)) {  
126         
127         // Yup, convert it to a Vector object and return it.
128         return vertexFromChampObject(retrievedVertex.get(),type);
129
130       } else {
131
132         // We didn't find a vertex with the supplied id, so just throw an
133         // exception.
134         throw new CrudException("No vertex with id " + id + " found in graph",
135             javax.ws.rs.core.Response.Status.NOT_FOUND);
136       }
137
138     } catch (ChampUnmarshallingException e) {
139
140       // Something went wrong - throw an exception.
141       throw new CrudException(e.getMessage(),
142           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
143     }
144   }
145
146
147   @Override
148   public List<Edge> getVertexEdges(String id) throws CrudException {
149
150     if (logger.isDebugEnabled()) {
151       logger.debug("get Edges incident to vertex with id: " + id + " from graph");
152     }
153
154     try {
155       long idAsLong = Long.parseLong(id); // GDF - what to do about id???
156
157       // Request the vertex from the graph db.
158       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
159
160       // Did we find it?
161       if (retrievedVertex.isPresent()) {
162
163         // Query the Champ library for the edges which are incident to the specified
164         // vertex.
165         Stream<ChampRelationship> relationships =
166             champApi.retrieveRelationships(retrievedVertex.get());
167
168         // Build an edge list from the result stream.
169         List<Edge> edges = new ArrayList<Edge>();
170         relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
171
172         return edges;
173
174       } else {
175
176         // We couldn't find the specified vertex, so throw an exception.
177         throw new CrudException("No vertex with id " + id + " found in graph",
178             javax.ws.rs.core.Response.Status.NOT_FOUND);
179       }
180
181     } catch (ChampUnmarshallingException e) {
182
183       // Something went wrong, so throw an exception.
184       throw new CrudException(e.getMessage(),
185           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
186
187     } catch (ChampObjectNotExistsException e) {
188
189       // We couldn't find the specified vertex, so throw an exception.
190       throw new CrudException("No vertex with id " + id + " found in graph",
191           javax.ws.rs.core.Response.Status.NOT_FOUND);
192     }
193   }
194
195
196   @Override
197   public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
198
199     if (logger.isDebugEnabled()) {
200       logger.debug("Add/update vertex: {label: " + type
201           + " properties:" + propertiesMapToString(properties));
202     }
203
204     //Add the aai_node_type so that AAI can read the data created by gizmo
205     properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
206
207     // Create an object to represent our vertex in the format expected by the Champ library.
208     ChampObject objectToCreate = buildChampObject(type, properties);
209
210     try {
211
212       // Ask the Champ library to store our vertex, placing the returned object into a
213       // list so that we can easily put that into our result object.
214       return vertexFromChampObject(champApi.storeObject(objectToCreate),type);
215
216     } catch (ChampMarshallingException
217         | ChampSchemaViolationException
218         | ChampObjectNotExistsException e) {
219
220       // Something went wrong - throw an exception.
221       throw new CrudException(e.getMessage(),
222           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
223     }
224   }
225
226
227   @Override
228   public Vertex updateVertex(String id, String type, Map<String, Object> properties)
229       throws CrudException {
230
231     if (logger.isDebugEnabled()) {
232       logger.debug("Update vertex with id: " + id + " with properties: "
233           + propertiesMapToString(properties));
234     }
235     //Add the aai_node_type so that AAI can read the data created by gizmo
236     properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
237
238     try {
239       // Now, build the updated version of the Champ Object...
240       ChampObject updateObject = buildChampObject(id, type, properties);
241       // ...and send it to the Champ library.
242       return vertexFromChampObject(champApi.replaceObject(updateObject),type);
243
244     } catch (ChampObjectNotExistsException e) {
245       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
246     } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
247       throw new CrudException(e.getMessage(),
248           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
249     }
250
251   }
252
253
254   @Override
255   public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
256
257     if (logger.isDebugEnabled()) {
258       logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
259           + propertiesMapToString(filter));
260     }
261
262
263     filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
264
265
266     Stream<ChampObject> retrievedVertices = champApi.queryObjects(filter);
267
268     List<Vertex> vertices = retrievedVertices
269         .map(v -> vertexFromChampObject(v,type))
270         .collect(Collectors.toList());
271
272
273     if (logger.isDebugEnabled()) {
274       logger.debug("Resulting vertex list: " + retrievedVertices);
275     }
276
277     // ...and return it to the caller.
278     return vertices;
279   }
280
281   private Object getRelKey(String id) {
282     Object key = id;
283     // convert into Long if applicable . TODO : revisit in story NUC-304
284     try {
285       key = Long.parseLong(id);
286     } catch (NumberFormatException e) {
287       // The id isn't a Long, leave it as a string
288     }
289
290     return key;
291   }
292
293   @Override
294   public Edge getEdge(String id, String type) throws CrudException {
295
296     if (logger.isDebugEnabled()) {
297       logger.debug("Get edge with id: " + id);
298     }
299
300     try {
301
302       // Request the edge from the graph db.
303       Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
304
305       // Did we find it?
306       if (relationship.isPresent() && relationship.get().getType().equals(type)) {
307
308         // Yup - return the result.
309         return edgeFromChampRelationship(relationship.get());
310
311       } else {
312
313         // We didn't find an edge with the supplied id, so throw an exception.
314         throw new CrudException("No edge with id " + id + " found in graph",
315             javax.ws.rs.core.Response.Status.NOT_FOUND);
316       }
317
318     } catch (ChampUnmarshallingException e) {
319
320       // Something went wrong, so throw an exception.
321       throw new CrudException(e.getMessage(),
322           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
323     }
324   }
325
326   @Override
327   public Edge addEdge(String type,
328                       Vertex source,
329                       Vertex target,
330                       Map<String, Object> properties) throws CrudException {
331
332     // For now, assume source and target are straight ids...
333     try {
334
335       Optional<ChampObject> sourceObject
336           = 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
339             + " does not exist in graph data base",
340             javax.ws.rs.core.Response.Status.BAD_REQUEST);
341       }
342
343       Optional<ChampObject> targetObject
344           = champApi.retrieveObject(Long.parseLong(target.getId().get()));
345       if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
346         throw new CrudException("Error creating edge - target vertex with id " + target
347             + " does not exist in graph data base",
348             javax.ws.rs.core.Response.Status.BAD_REQUEST);
349       }
350
351       // Now, create the ChampRelationship object for our edge and store it in
352       // the graph database.
353       return edgeFromChampRelationship(
354           champApi.storeRelationship(
355               new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type)
356                   .properties(properties)
357                   .build()));
358
359     } catch (ChampMarshallingException
360         | ChampObjectNotExistsException
361         | ChampSchemaViolationException
362         | ChampRelationshipNotExistsException
363         | ChampUnmarshallingException e) {
364
365       throw new CrudException("Error creating edge: " + e.getMessage(),
366           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
367     }
368   }
369
370
371   @Override
372   public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
373
374     filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
375
376     Stream<ChampRelationship> retrievedRelationships = champApi.queryRelationships(filter);
377     // Process the result stream from the Champ library into an Edge list, keeping only
378     // edges of the specified type.
379     List<Edge> edges = retrievedRelationships
380         .map(r -> edgeFromChampRelationship(r))
381         .collect(Collectors.toList());
382
383     return edges;
384   }
385
386   @Override
387   public Edge updateEdge(Edge edge) throws CrudException {
388
389     if (logger.isDebugEnabled()) {
390       logger.debug("Update edge with id: " + edge.getId() + " with properties: "
391           + propertiesMapToString(edge.getProperties()));
392     }
393
394     try {
395       // Now, build the updated version of the Champ Relationship...
396       ChampRelationship updateRelationship = new ChampRelationship.Builder(
397           buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
398               edge.getSource().getProperties()),
399           buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
400               edge.getTarget().getProperties()),
401           edge.getType()).key(getRelKey(edge.getId().get()))
402           .properties(edge.getProperties()).build();
403       // ...and send it to the Champ library.
404       return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
405
406
407     } catch (ChampRelationshipNotExistsException ex) {
408       throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
409     } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException
410         | ChampSchemaViolationException ex) {
411
412       throw new CrudException(ex.getMessage(),
413           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
414     }
415   }
416
417   @Override
418   public void deleteVertex(String id, String type) throws CrudException {
419
420     try {
421
422       // First, retrieve the vertex that we intend to delete.
423       Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
424
425       // Did we find it?
426       if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
427         throw new CrudException("Failed to delete vertex with id: "
428             + id + " - vertex does not exist.",
429             javax.ws.rs.core.Response.Status.NOT_FOUND);
430       }
431
432       // Now, verify that there are no edges incident to the vertex (they must be deleted
433       // first if so).
434       Stream<ChampRelationship> relationships =
435           champApi.retrieveRelationships(retrievedVertex.get());
436
437       if (relationships.count() > 0) {
438         throw new CrudException("Attempt to delete vertex with id "
439             + id + " which has incident edges.",
440             javax.ws.rs.core.Response.Status.BAD_REQUEST);
441       }
442
443       // Finally, we can attempt to delete our vertex.
444       champApi.deleteObject(Long.parseLong(id));
445
446
447     } catch (NumberFormatException
448         | ChampUnmarshallingException
449         | ChampObjectNotExistsException e) {
450
451       throw new CrudException(e.getMessage(),
452           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
453     }
454   }
455
456   @Override
457   public void deleteEdge(String id, String type) throws CrudException {
458
459     try {
460
461       // First, retrieve the edge that we want to delete.
462       Optional<ChampRelationship> relationshipToDelete
463           = champApi.retrieveRelationship(getRelKey(id));
464
465
466       // Did we find it?
467       if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
468         throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
469             javax.ws.rs.core.Response.Status.NOT_FOUND);
470       }
471
472       // Now we can delete the edge.
473       champApi.deleteRelationship(relationshipToDelete.get());
474
475     } catch (ChampRelationshipNotExistsException
476         | NumberFormatException
477         | ChampUnmarshallingException e) {
478
479       throw new CrudException(e.getMessage(),
480           javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
481     }
482   }
483
484
485   /**
486    * This helper method generates a string representation of a properties map for
487    * logging purposes.
488    *
489    * @param properties - 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   /**
508    * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
509    *
510    * @param type       - The type to assign to our ChampObject
511    * @param properties - The set of properties to assign to our ChampObject
512    * @return - A populated ChampObject
513    */
514   private ChampObject buildChampObject(String type, Map<String, Object> properties) {
515
516     ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
517         .ofType(type)
518         .withoutKey();
519
520     for (String key : properties.keySet()) {
521       objectInProgress.withProperty(key, properties.get(key));
522     }
523     return objectInProgress.build();
524   }
525
526
527   /**
528    * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
529    *
530    * @param id         - Unique identifier for this object.
531    * @param type       - The type to assign to our ChampObject
532    * @param properties - The set of properties to assign to our ChampObject
533    * @return - A populated ChampObject
534    */
535   private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
536
537     ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
538         .ofType(type)
539         .withKey(Long.parseLong(id));
540
541     for (String key : properties.keySet()) {
542       objectInProgress.withProperty(key, properties.get(key));
543     }
544     return objectInProgress.build();
545   }
546
547
548   
549   
550   private Vertex vertexFromChampObject(ChampObject champObject, String type) {
551
552     // Get the identifier for this vertex from the Champ object.
553     Object id = champObject.getKey().orElse("");
554
555     // Start building our {@link Vertex} object.
556     Vertex.Builder vertexBuilder = new Vertex.Builder(type);
557     vertexBuilder.id(id.toString());
558
559     // Convert the properties associated with the Champ object into the form expected for
560     // a Vertex object.
561     for (String key : champObject.getProperties().keySet()) {
562       vertexBuilder.property(key, champObject.getProperties().get(key));
563     }
564
565     // ...and return it.
566     return vertexBuilder.build();
567   }
568
569
570   /**
571    * This helper method converts a {@link ChampRelationship} from the Champ library into an
572    * equivalent {@link Edge} object that is understood by the CRUD Service.
573    *
574    * @param relationship - 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())
583         .id(relationshipId.toString());
584     edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
585         relationship.getSource().getProperties()
586             .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
587                 ? relationship.getSource().getType()
588                 : relationship.getSource().getProperties()
589                     .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
590     edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
591         relationship.getTarget().getProperties()
592             .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
593                 ? relationship.getTarget().getType()
594                 : relationship.getTarget().getProperties()
595                     .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
596
597     for (String key : relationship.getProperties().keySet()) {
598       edgeBuilder.property(key, relationship.getProperties().get(key).toString());
599     }
600
601     return edgeBuilder.build();
602   }
603
604
605   /**
606    * Performs all one-time configuration operations which are required when creating
607    * a new instance of the DAO.
608    */
609   private void configure() {
610
611     // Instantiate the Champ library API.
612     try {
613
614       // Determine which back end we are using.
615       switch (getBackendTypeFromConfig()) {
616
617         case IN_MEMORY:
618
619           logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO,
620               "In Memory",
621               daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
622               "Not applicable");
623
624           champApi = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY,
625               daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME));
626
627           break;
628
629         case TITAN:
630           try {
631             String db = daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB);
632             Short graphIdSuffix = (short) new Random().nextInt(Short.MAX_VALUE);
633             logger.info(CrudServiceMsgs.TITAN_GRAPH_INFO, GRAPH_UNQ_INSTANCE_ID_SUFFIX
634                 + ": = " + graphIdSuffix);
635             if (db.equalsIgnoreCase(STORAGE_CASSANDRA_DB)) {
636               logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with cassandra backend",
637                   daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
638                   daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
639
640               TitanChampGraphImpl.Builder champApiBuilder =
641                   new TitanChampGraphImpl.Builder(daoConfig.getProperty(CONFIG_GRAPH_NAME,
642                       DEFAULT_GRAPH_NAME))
643                       .property("storage.backend", "cassandrathrift")
644                       .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
645                       .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
646
647               if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
648                 champApiBuilder.property("champ.event.stream.publisher",
649                     daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
650               }
651
652               if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
653                 champApiBuilder.property("champ.event.stream.publisher-pool-size",
654                     daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
655               }
656
657               champApi = champApiBuilder.build();
658
659             } else if (db.equalsIgnoreCase(STORAGE_HBASE_DB)) {
660
661               logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with Hbase backend",
662                   daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
663                   daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
664               TitanChampGraphImpl.Builder champApiBuilder =
665                   new TitanChampGraphImpl.Builder(daoConfig
666                       .getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME))
667                       .property("storage.backend", "hbase")
668                       .property("storage.hbase.ext.zookeeper.znode.parent",
669                           daoConfig.get(CONFIG_HBASE_ZNODE_PARENT))
670                       .property("storage.port", daoConfig.get(CONFIG_STORAGE_PORT))
671                       .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
672                       .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
673
674               if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
675                 champApiBuilder.property("champ.event.stream.publisher",
676                     daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
677               }
678
679               if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
680                 champApiBuilder.property("champ.event.stream.publisher-pool-size",
681                     daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
682               }
683               champApi = champApiBuilder.build();
684             } else {
685               logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
686                   daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB));
687             }
688
689           } catch (com.thinkaurelius.titan.core.TitanException e) {
690
691             logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, "Titan", e.getMessage());
692           }
693
694
695           break;
696
697         default:
698           logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
699               daoConfig.getProperty(CONFIG_STORAGE_BACKEND));
700           break;
701       }
702
703     } catch (CrudException e) {
704       logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR,
705           daoConfig.getProperty(CONFIG_STORAGE_BACKEND), e.getMessage());
706     }
707   }
708
709
710   /**
711    * Performs any necessary shut down operations when the DAO is no longer needed.
712    */
713   public void close() {
714
715     if (champApi != null) {
716
717       logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);
718
719       champApi.shutdown();
720     }
721   }
722
723
724   /**
725    * This helper function converts the 'graph back end type' config parameter into the
726    * corresponding {@link ChampAPI.Type}.
727    *
728    * @return - A {@link ChampAPI.Type}
729    * @throws CrudException
730    */
731   private ChampGraph.Type getBackendTypeFromConfig() throws CrudException {
732
733     // Get the back end type from the DAO's configuration properties.
734     String backend = daoConfig.getProperty(CONFIG_STORAGE_BACKEND, "in-memory");
735
736     // Now, find the appropriate ChampAPI type and return it.
737     if (backend.equals("in-memory")) {
738       return ChampGraph.Type.IN_MEMORY;
739     } else if (backend.equals("titan")) {
740       return ChampGraph.Type.TITAN;
741     }
742
743     // If we are here, then whatever was in the config properties didn't match to a supported
744     // back end type, so just throw an exception and let the caller figure it out.
745     throw new CrudException("Invalid graph backend type '" + backend + "' specified.",
746         javax.ws.rs.core.Response.Status.BAD_REQUEST);
747   }
748
749
750 }