Renaming openecomp to onap
[aai/champ.git] / src / main / java / org / onap / aai / champ / graph / impl / AbstractTinkerpopChampGraph.java
1 /**
2  * ============LICENSE_START==========================================
3  * org.onap.aai
4  * ===================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ===================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END============================================
20  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.onap.aai.champ.graph.impl;
23
24 import java.io.IOException;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.NoSuchElementException;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.Spliterator;
34 import java.util.Spliterators;
35 import java.util.concurrent.atomic.AtomicBoolean;
36 import java.util.stream.Stream;
37 import java.util.stream.StreamSupport;
38
39 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
40 import org.apache.tinkerpop.gremlin.structure.Direction;
41 import org.apache.tinkerpop.gremlin.structure.Edge;
42 import org.apache.tinkerpop.gremlin.structure.Graph;
43 import org.apache.tinkerpop.gremlin.structure.Property;
44 import org.apache.tinkerpop.gremlin.structure.Vertex;
45 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
46 import org.onap.aai.champ.event.ChampEvent;
47 import org.onap.aai.champ.event.ChampEvent.ChampOperation;
48 import org.onap.aai.champ.exceptions.ChampMarshallingException;
49 import org.onap.aai.champ.exceptions.ChampObjectNotExistsException;
50 import org.onap.aai.champ.exceptions.ChampRelationshipNotExistsException;
51 import org.onap.aai.champ.exceptions.ChampSchemaViolationException;
52 import org.onap.aai.champ.exceptions.ChampUnmarshallingException;
53 import org.onap.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
54 import org.onap.aai.champ.model.ChampObject;
55 import org.onap.aai.champ.model.ChampPartition;
56 import org.onap.aai.champ.model.ChampRelationship;
57 import org.onap.aai.champ.model.ChampSchema;
58 import org.onap.aai.champ.model.fluent.partition.CreateChampPartitionable;
59 import org.onap.aai.champ.transform.TinkerpopChampformer;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 import com.fasterxml.jackson.core.JsonProcessingException;
64 import com.fasterxml.jackson.databind.ObjectMapper;
65
66 public abstract class AbstractTinkerpopChampGraph extends AbstractValidatingChampGraph {
67         
68         private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTinkerpopChampGraph.class);
69         private static final TinkerpopChampformer TINKERPOP_CHAMPFORMER = new TinkerpopChampformer();
70         private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
71
72         private static final int COMMIT_RETRY_COUNT = 3;
73
74         private volatile AtomicBoolean isShutdown;
75
76         protected AbstractTinkerpopChampGraph(Map<String, Object> properties) {
77           super(properties);
78           
79           isShutdown = new AtomicBoolean(false);
80       Runtime.getRuntime().addShutdownHook(shutdownHook);
81         }
82         
83         private static final TinkerpopChampformer getChampformer() {
84                 return TINKERPOP_CHAMPFORMER;
85         }
86
87         private static final ObjectMapper getObjectMapper() {
88                 return OBJECT_MAPPER;
89         }
90
91         private Vertex writeVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
92                 final Vertex vertex;
93                 
94                 if (object.getKey().isPresent()) {
95                         final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
96
97                         if (vertexIter.hasNext()) {
98                                 vertex = vertexIter.next();
99                         } else throw new ChampObjectNotExistsException();
100                 } else {
101                         vertex = getGraph().addVertex(object.getType());
102                 }
103
104                 for (Entry<String, Object> property : object.getProperties().entrySet()) {
105
106                         if (property.getValue() instanceof List) {
107                                 for (Object subPropertyValue : (List<?>) property.getValue()) {
108                                         vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
109                                 }
110                         } else if (property.getValue() instanceof Set) {
111                                 for (Object subPropertyValue : (Set<?>) property.getValue()) {
112                                         vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
113                                 }
114                         } else {
115                                 vertex.property(property.getKey(), property.getValue());
116                         }
117                 }
118
119                 return vertex;
120         }
121         
122         private Vertex replaceVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
123                 Vertex vertex;
124                 
125                 if (object.getKey().isPresent()) {
126                         final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
127
128                         if (vertexIter.hasNext()) {
129                                 vertex = vertexIter.next();
130                         } else throw new ChampObjectNotExistsException();
131                 } else {
132                         throw new ChampObjectNotExistsException();
133                 }
134
135                 //clear all the existing properties
136                 Iterator<VertexProperty<Object>> it = vertex.properties();
137                 while (it.hasNext()) {
138                         it.next().remove();
139                 }
140                 
141                 for (Entry<String, Object> property : object.getProperties().entrySet()) {
142
143                         if (property.getValue() instanceof List) {
144                                 for (Object subPropertyValue : (List<?>) property.getValue()) {
145                                         vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
146                                 }
147                         } else if (property.getValue() instanceof Set) {
148                                 for (Object subPropertyValue : (Set<?>) property.getValue()) {
149                                         vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
150                                 }
151                         } else {
152                                 vertex.property(property.getKey(), property.getValue());                                
153                         }
154                 }
155
156                 return vertex;
157         }
158
159         private Edge writeEdge(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
160
161                 final Vertex source = writeVertex(relationship.getSource());
162                 final Vertex target = writeVertex(relationship.getTarget());
163                 final Edge edge;
164
165                 if (relationship.getKey().isPresent()) {
166                         final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
167
168                         if (edgeIter.hasNext()) {
169                                 edge = edgeIter.next();
170                         } else throw new ChampRelationshipNotExistsException();
171                 } else {
172                         edge = source.addEdge(relationship.getType(), target);
173                 }
174
175                 for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
176                         edge.property(property.getKey(), property.getValue());
177                 }
178
179                 return edge;
180         }
181         
182         private Edge replaceEdge(ChampRelationship relationship) throws  ChampRelationshipNotExistsException, ChampMarshallingException {
183                 final Edge edge;
184                 
185                 if(!relationship.getSource().getKey().isPresent() || !relationship.getTarget().getKey().isPresent()){
186                         throw new IllegalArgumentException("Invalid source/target");
187                 }
188                 
189                 if (relationship.getKey().isPresent()) {
190                         final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
191
192                         if (edgeIter.hasNext()) {
193                                 edge = edgeIter.next();
194                                 //validate if the source/target are the same as before. Throw error if not the same
195                                 if (!edge.outVertex().id().equals(relationship.getSource().getKey().get())
196                                                 || !edge.inVertex().id().equals(relationship.getTarget().getKey().get())) {
197                                         throw new IllegalArgumentException("source/target can't be updated");
198                                 }
199
200                         } else throw new ChampRelationshipNotExistsException();
201                 } else {
202                         throw new ChampRelationshipNotExistsException();
203                 }
204                 
205                 // clear all the existing properties
206                 Iterator<Property<Object>> it = edge.properties();
207                 while (it.hasNext()) {
208                         it.next().remove();
209                 }
210                                 
211                 for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
212                         edge.property(property.getKey(), property.getValue());
213                 }
214
215                 return edge;
216         }
217
218         private void tryRollback() {
219                 if (getGraph().features().graph().supportsTransactions()) {
220                         getGraph().tx().rollback();
221                 }
222         }
223
224         private void tryCommit() {
225
226                 if (getGraph().features().graph().supportsTransactions()) {
227
228                         final long initialBackoff = (int) (Math.random() * 50);
229
230                         for (int i = 0; i < COMMIT_RETRY_COUNT; i++) {
231                                 try {
232                                         getGraph().tx().commit();
233                                         return;
234                                 } catch (Throwable e) {
235                                         if (i == COMMIT_RETRY_COUNT - 1) {
236                                                 LOGGER.error("Maxed out commit attempt retries, client must handle exception and retry", e);
237                                                 getGraph().tx().rollback();
238                                                 throw e;
239                                         }
240
241                                         final long backoff = (long) Math.pow(2, i) * initialBackoff;
242                                         LOGGER.warn("Caught exception while retrying transaction commit, retrying in " + backoff + " ms");
243                                         
244                                         try {
245                                                 Thread.sleep(backoff);
246                                         } catch (InterruptedException ie) {
247                                                 LOGGER.info("Interrupted while backing off on transaction commit");
248                                                 return;
249                                         }
250                                 }
251                         }
252                 }
253         }
254
255         protected abstract Graph getGraph();
256
257         private Thread shutdownHook = new Thread() {
258                 @Override
259                 public void run() {
260                         try {
261                                 shutdown();
262                         } catch (IllegalStateException e) {
263                                 //Suppress, because shutdown() has already been called
264                         }
265                 }
266         };
267
268         protected boolean isShutdown() {
269                 return isShutdown.get();
270         }
271
272         @Override
273         public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
274                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
275         
276                 //If they provided the object key, do this the quick way rather than creating a traversal
277                 if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
278                         try {
279                                 final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
280                         
281                                 if (object.isPresent()) return Stream.of(object.get());
282                                 else return Stream.empty();
283                         } catch (ChampUnmarshallingException e) {
284                                 LOGGER.warn("Failed to unmarshall object", e);
285                                 return Stream.empty();
286                         }
287                 }
288
289                 final GraphTraversal<Vertex, Vertex> query = getGraph().traversal().V();
290
291                 for (Entry<String, Object> filter : queryParams.entrySet()) {
292                         if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
293                                 continue; //For performance reasons, the label is the last thing to be added
294                         } else {
295                                 query.has(filter.getKey(), filter.getValue());
296                         }
297                 }
298
299                 if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
300                         query.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
301                 }
302
303                 final Iterator<ChampObject> objIter = new Iterator<ChampObject> () {
304         
305                         private ChampObject next;
306
307
308                         @Override
309                         public boolean hasNext() {
310                                 while (query.hasNext()) {
311                                         try {
312                                                 next = getChampformer().unmarshallObject(query.next());
313                                                 return true;
314                                         } catch (ChampUnmarshallingException e) {
315                                                 LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
316                                         }                                       
317                                 }
318
319                                 tryCommit(); //Danger ahead if this iterator is not completely consumed
320                                                                                                                 //then the transaction cache will hold stale values
321
322                                 next = null;
323                                 return false;
324                         }
325
326                         @Override
327                         public ChampObject next() {
328                                 if (next == null) throw new NoSuchElementException();
329                                 
330                                 return next;
331                         }
332                 };
333
334                 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
335                 objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
336         }
337
338         @Override
339         public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
340                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
341
342                 final Iterator<Vertex> vertices = getGraph().vertices(key);
343                 final Optional<ChampObject> optionalObject;
344
345                 if (!vertices.hasNext()) optionalObject = Optional.empty();
346                 else optionalObject = Optional.of(getChampformer().unmarshallObject(vertices.next()));
347
348                 tryCommit();
349
350                 return optionalObject;
351         }
352
353         @Override
354         public Stream<ChampRelationship> retrieveRelationships(ChampObject source) throws ChampUnmarshallingException, ChampObjectNotExistsException {
355                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
356                 
357                 final Vertex sourceVertex;
358
359                 try {
360                         sourceVertex = getGraph().vertices(source.getKey().get()).next();
361                 } catch (NoSuchElementException e) {                    
362                         tryRollback();
363
364                         throw new ChampObjectNotExistsException();
365                 }
366
367                 final Iterator<Edge> edges = sourceVertex.edges(Direction.BOTH);
368                 final Iterator<ChampRelationship> relIter = new Iterator<ChampRelationship> () {
369
370                         private ChampRelationship next;
371
372                         @Override
373                         public boolean hasNext() {
374                                 while (edges.hasNext()) {
375                                         try {
376                                                 next = getChampformer().unmarshallRelationship(edges.next());
377                                                 return true;
378                                         } catch (ChampUnmarshallingException e) {
379                                                 LOGGER.warn("Failed to unmarshall tinkerpop edge during query, returning partial results", e);
380                                         }                                       
381                                 }
382
383                                 tryCommit();//Danger ahead if this iterator is not completely
384                                                                          //consumed, then the transaction cache will be stale
385                                 next = null;
386                                 return false;
387                         }
388
389                         @Override
390                         public ChampRelationship next() {
391                                 if (next == null) throw new NoSuchElementException();
392                                 
393                                 return next;
394                         }
395                 };
396
397                 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
398                 relIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
399         }
400
401         @Override
402         public ChampObject doStoreObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
403
404                 try {
405                         final Vertex vertex = writeVertex(object);
406
407                         tryCommit();
408                         
409                         return ChampObject.create()
410                                 .from(object)
411                                 .withKey(vertex.id())
412                                 .build();
413                         
414                 } catch (ChampObjectNotExistsException e) {
415                         tryRollback();
416
417                         throw e;
418                 }
419         }
420         
421         @Override
422         public ChampObject doReplaceObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
423
424                 try {
425                         final Vertex vertex = replaceVertex(object);
426
427                         tryCommit();
428                         
429                         return ChampObject.create()
430                                   .from(object)
431                                   .withKey(vertex.id())
432                                   .build();
433                         
434                 } catch (ChampObjectNotExistsException e) {
435                         tryRollback();
436
437                         throw e;
438                 }
439         }
440
441         public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
442                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
443
444                 final Iterator<Vertex> vertex = getGraph().vertices(key);
445
446                 if (!vertex.hasNext()) {
447                         tryRollback();
448
449                         throw new ChampObjectNotExistsException();
450                 }
451
452                 vertex.next().remove();
453                 
454                 tryCommit();
455         }
456
457         @Override
458         public ChampRelationship doStoreRelationship(ChampRelationship relationship)
459                         throws ChampUnmarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException  {
460
461                 try {
462                         final Edge edge = writeEdge(relationship);
463
464                         tryCommit();
465         
466                         return getChampformer().unmarshallRelationship(edge);
467                         
468                 } catch (ChampObjectNotExistsException | ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
469                         tryRollback();
470
471                         throw e;
472                 }
473         }
474         
475         @Override
476         public ChampRelationship doReplaceRelationship(ChampRelationship relationship)
477                         throws ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampMarshallingException  {
478
479                 try {
480                         final Edge edge = replaceEdge(relationship);
481
482                         tryCommit();
483         
484                         return getChampformer().unmarshallRelationship(edge);
485                         
486                 } catch ( ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
487                         tryRollback();
488
489                         throw e;
490                 }
491         }
492
493         @Override
494         public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
495                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
496
497                 //If they provided the relationship key, do this the quick way rather than creating a traversal
498                 if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
499                         try {
500                                 final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
501                         
502                                 if (relationship.isPresent()) return Stream.of(relationship.get());
503                                 else return Stream.empty();
504                         } catch (ChampUnmarshallingException e) {
505                                 LOGGER.warn("Failed to unmarshall relationship", e);
506                                 return Stream.empty();
507                         }
508                 }
509          
510                 final GraphTraversal<Edge, Edge> query = getGraph().traversal().E();
511
512                 for (Entry<String, Object> filter : queryParams.entrySet()) {
513                         if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
514                                 continue; //Add the label last for performance reasons
515                         } else {
516                                 query.has(filter.getKey(), filter.getValue());
517                         }
518                 }
519
520                 if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
521                         query.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
522                 }
523
524                 final Iterator<ChampRelationship> objIter = new Iterator<ChampRelationship> () {
525         
526                         private ChampRelationship next;
527
528
529                         @Override
530                         public boolean hasNext() {
531                                 while (query.hasNext()) {
532                                         try {
533                                                 next = getChampformer().unmarshallRelationship(query.next());
534                                                 return true;
535                                         } catch (ChampUnmarshallingException e) {
536                                                 LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
537                                         }                                       
538                                 }
539
540                                 tryCommit(); //Danger ahead if this iterator is not completely
541                                                                           //consumed, then the transaction cache will be stale
542                                         
543                                 next = null;
544                                 return false;
545                         }
546
547                         @Override
548                         public ChampRelationship next() {
549                                 if (next == null) throw new NoSuchElementException();
550                                 
551                                 return next;
552                         }
553                 };
554
555                 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
556                 objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
557         }
558
559         @Override
560         public Optional<ChampRelationship> retrieveRelationship(Object key)
561                         throws ChampUnmarshallingException {
562                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
563
564                 final Iterator<Edge> edge = getGraph().edges(key);
565                 final Optional<ChampRelationship> optionalRelationship;
566
567                 if (!edge.hasNext()) optionalRelationship = Optional.empty();
568                 else optionalRelationship = Optional.of(getChampformer().unmarshallRelationship(edge.next()));
569
570                 tryCommit();
571
572                 return optionalRelationship;
573         }
574
575         public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
576                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
577                 if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
578
579                 final Iterator<Edge> edge = getGraph().edges(relationship.getKey().get());
580                 
581                 if (!edge.hasNext()) {
582                         tryRollback();
583
584                         throw new ChampRelationshipNotExistsException();
585                 }
586                 
587                 edge.next().remove();
588
589                 tryCommit();
590         }
591
592         @Override
593         public ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampMarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException {
594                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
595
596                 try {
597                         final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
598                         final CreateChampPartitionable storedPartition = ChampPartition.create();
599
600                         for (ChampObject champObject : submittedPartition.getChampObjects()) {
601                                 final Vertex vertex = writeVertex(champObject);
602                                 objectsWithKeys.put(champObject, ChampObject.create()
603                                                                                                                         .from(champObject)
604                                                                                                                         .withKey(vertex.id())
605                                                                                                                         .build());
606                         }
607
608                         for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
609                                 if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
610                                         final Vertex vertex = writeVertex(champRelationship.getSource());
611
612                                         objectsWithKeys.put(champRelationship.getSource(), ChampObject.create()
613                                                                                                                 .from(champRelationship.getSource())
614                                                                                                                 .withKey(vertex.id())
615                                                                                                                 .build());
616                                 }
617
618                                 if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
619                                         final Vertex vertex = writeVertex(champRelationship.getTarget());
620
621                                         objectsWithKeys.put(champRelationship.getTarget(), ChampObject.create()
622                                                                                                                 .from(champRelationship.getTarget())
623                                                                                                                 .withKey(vertex.id())
624                                                                                                                 .build());
625                                 }
626
627                                 final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
628                                                                                                                                                                                         objectsWithKeys.get(champRelationship.getTarget()),
629                                                                                                                                                                                         champRelationship.getType());
630
631                                 if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
632                                 
633                                 relWithKeysBuilder.properties(champRelationship.getProperties());
634
635                                 final Edge edge = writeEdge(relWithKeysBuilder.build());
636
637                                 storedPartition.withRelationship(ChampRelationship.create()
638                                                                                                                                         .from(champRelationship)
639                                                                                                                                         .withKey(edge.id())
640                                                                                                                                         .build());
641                         }
642
643                         for (ChampObject object : objectsWithKeys.values()) {
644                                 storedPartition.withObject(object);
645                         }
646
647                         tryCommit();
648             
649                         return storedPartition.build();
650                         
651                 } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
652                         tryRollback();
653
654                         throw e;
655                 }
656         }
657
658         public void executeDeletePartition(ChampPartition graph) {
659                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
660
661                 for (ChampObject champObject : graph.getChampObjects()) {
662                         try {
663                                 final Object vertexId = champObject.getKey().get();
664                                 final Iterator<Vertex> vertex = getGraph().vertices(vertexId);
665         
666                                 if (vertex.hasNext()) {
667                                         vertex.next().remove();
668                                 }
669                         } catch (NoSuchElementException e) {
670                                 tryRollback();
671
672                                 throw new IllegalArgumentException("Must pass a key to delete an object");
673                         }
674                 }
675
676                 for (ChampRelationship champRelationship : graph.getChampRelationships()) {
677                         try {
678                                 final Iterator<Edge> edge = getGraph().edges(champRelationship.getKey().get());
679                 
680                                 if (edge.hasNext()) {
681                                         edge.next().remove();
682                                 }
683                         } catch (NoSuchElementException e) {
684                                 tryRollback();
685
686                                 throw new IllegalArgumentException("Must pass a key to delete a relationship");
687                         }
688                 }
689
690                 tryCommit();
691
692         }
693
694         @Override
695         public void shutdown() {
696
697                 if (isShutdown.compareAndSet(false, true)) {
698                   super.shutdown();
699                         try {
700                                 getGraph().close();
701                         } catch (Throwable t) {
702                                 LOGGER.error("Exception while shutting down graph", t);
703                         }
704                 } else {
705                         throw new IllegalStateException("Cannot call shutdown() after shutdown() was already initiated");
706                 }
707         }
708
709         @Override
710         public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
711                 if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
712
713                 if (getGraph().features().graph().variables().supportsVariables()) {
714                         try {
715                                 getGraph().variables().set("schema", getObjectMapper().writeValueAsBytes(schema));
716                         } catch (JsonProcessingException e) {
717                                 throw new RuntimeException(e);
718                         }
719                 } else {
720                         super.storeSchema(schema);
721                 }
722         }
723
724         @Override
725         public ChampSchema retrieveSchema() {
726                 if (isShutdown()) throw new IllegalStateException("Cannot call retrieveSchema() after shutdown has been initiated");
727
728                 if (getGraph().features().graph().variables().supportsVariables()) {
729                         final Optional<byte[]> schema = getGraph().variables().get("schema");
730
731                         if (schema.isPresent()) {
732                                 try {
733                                         return getObjectMapper().readValue(schema.get(), ChampSchema.class);
734                                 } catch (IOException e) {
735                                         throw new RuntimeException(e);
736                                 }
737                         }
738                 }
739
740                 return super.retrieveSchema();
741         }
742
743         @Override
744         public void deleteSchema() {
745                 if (isShutdown()) throw new IllegalStateException("Cannot call deleteSchema() after shutdown has been initiated");
746
747                 if (getGraph().features().graph().variables().supportsVariables()) {
748                         getGraph().variables().remove("schema");
749                 } else {
750                         super.deleteSchema();
751                 }
752         }
753 }