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