Merge "Added @Override annotation above signature"
[aai/champ.git] / src / main / java / org / onap / aai / champ / graph / impl / AbstractGremlinChampGraph.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.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Optional;
29 import java.util.Set;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.structure.Edge;
36 import org.apache.tinkerpop.gremlin.structure.Vertex;
37 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
38 import org.onap.aai.champ.exceptions.ChampMarshallingException;
39 import org.onap.aai.champ.exceptions.ChampObjectNotExistsException;
40 import org.onap.aai.champ.exceptions.ChampRelationshipNotExistsException;
41 import org.onap.aai.champ.exceptions.ChampUnmarshallingException;
42 import org.onap.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
43 import org.onap.aai.champ.model.ChampElement;
44 import org.onap.aai.champ.model.ChampObject;
45 import org.onap.aai.champ.model.ChampPartition;
46 import org.onap.aai.champ.model.ChampRelationship;
47 import org.onap.aai.champ.model.fluent.partition.CreateChampPartitionable;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public abstract class AbstractGremlinChampGraph extends AbstractValidatingChampGraph {
52
53         private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGremlinChampGraph.class);
54
55         protected abstract GraphTraversalSource startTraversal();
56         protected abstract Stream<ChampElement> runTraversal(GraphTraversal<?, ?> traversal);
57
58         protected AbstractGremlinChampGraph(Map<String, Object> properties) {
59           super(properties);
60         }
61         
62         @Override
63         public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
64                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
65
66                 final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
67
68                 if (elements.count() == 0) {
69                         return Optional.empty();
70                 }
71
72                 return Optional.of(elements.findFirst().get().asObject());
73         }
74
75         public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
76                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
77
78                 final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
79
80                 if (elements.count() == 0) {
81                         throw new ChampObjectNotExistsException();
82                 }
83
84                 runTraversal(startTraversal().V(key).drop());
85         }
86
87         @Override
88         public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
89                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
90
91                 //If they provided the object key, do this the quick way rather than creating a traversal
92                 if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
93                         try {
94                                 final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
95                         
96                                 if (object.isPresent()) return Stream.of(object.get());
97                                 else return Stream.empty();
98                         } catch (ChampUnmarshallingException e) {
99                                 LOGGER.warn("Failed to unmarshall object", e);
100                                 return Stream.empty();
101                         }
102                 }
103
104                 final GraphTraversal<?, Vertex> traversal = startTraversal().V();
105
106                 for (Entry<String, Object> filter : queryParams.entrySet()) {
107                         if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
108                                 continue; //For performance reasons, the label is the last thing to be added
109                         } else {
110                                 traversal.has(filter.getKey(), filter.getValue());
111                         }
112                 }
113
114                 if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
115                         traversal.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
116                 }
117
118
119                 return runTraversal(traversal).map(element -> {
120                         return element.asObject(); //Safe, since we're only operating on vertices
121                 });
122         }
123
124         @Override
125         public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException {
126                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
127
128                 final Stream<ChampElement> elements = runTraversal(startTraversal().E(key).limit(1));
129
130                 if (elements.count() == 0) return Optional.empty();
131
132                 return Optional.of(elements.findFirst().get().asRelationship());
133         }
134
135         public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
136                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
137                 if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
138
139                 final Stream<ChampElement> elements = runTraversal(startTraversal().E(relationship.getKey().get()).limit(1));
140
141                 if (elements.count() == 0) {
142                         throw new ChampRelationshipNotExistsException();
143                 }
144
145                 runTraversal(startTraversal().E(relationship.getKey().get()).drop());
146         }
147
148         @Override
149         public Stream<ChampRelationship> retrieveRelationships(ChampObject object)
150                         throws ChampUnmarshallingException, ChampObjectNotExistsException {
151                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
152
153                 final Stream<ChampElement> elements = runTraversal(startTraversal().V(object.getKey().get()).limit(1).bothE());
154
155                 return elements.map(element -> {
156                         return element.asRelationship(); //Safe, since we're only operating on edges
157                 });
158         }
159
160         @Override
161         public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
162                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
163
164                 //If they provided the relationship key, do this the quick way rather than creating a traversal
165                 if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
166                         try {
167                                 final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
168                         
169                                 if (relationship.isPresent()) return Stream.of(relationship.get());
170                                 else return Stream.empty();
171                         } catch (ChampUnmarshallingException e) {
172                                 LOGGER.warn("Failed to unmarshall relationship", e);
173                                 return Stream.empty();
174                         }
175                 }
176
177                 final GraphTraversal<Edge, Edge> traversal = startTraversal().E();
178
179                 for (Entry<String, Object> filter : queryParams.entrySet()) {
180                         if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
181                                 continue; //Add the label last for performance reasons
182                         } else {
183                                 traversal.has(filter.getKey(), filter.getValue());
184                         }
185                 }
186
187                 if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
188                         traversal.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
189                 }
190
191                 return runTraversal(traversal).map(element -> {
192                         return element.asRelationship(); //Safe, since we are only operating on edges
193                 });
194         }
195
196         @Override
197         public void executeDeletePartition(ChampPartition graph) {
198                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
199
200                 final Object[] objectIds = graph.getChampObjects()
201                                 .stream()
202                                 .filter(o -> o.getKey().isPresent())
203                                 .map(o -> { return o.getKey().get(); })
204                                 .collect(Collectors.toList())
205                                 .toArray();
206
207                 final Object[] relationshipIds = graph.getChampRelationships()
208                                 .stream()
209                                 .filter(o -> o.getKey().isPresent())
210                                 .map(o -> { return o.getKey().get(); })
211                                 .collect(Collectors.toList())
212                                 .toArray();
213
214                 runTraversal(startTraversal().V(objectIds).drop());
215                 runTraversal(startTraversal().E(relationshipIds).drop());
216         }
217
218         @Override
219         protected ChampObject doStoreObject(ChampObject object)
220                         throws ChampMarshallingException, ChampObjectNotExistsException {
221                 final GraphTraversal<Vertex, Vertex> traversal;
222
223                 if (object.getKey().isPresent()) {
224                         traversal = startTraversal().V(object.getKey().get());
225                 } else {
226                         traversal = startTraversal().addV(object.getType());
227                 }
228
229                 for (Entry<String, Object> property : object.getProperties().entrySet()) {
230
231                         if (property.getValue() instanceof List) {
232                                 for (Object subPropertyValue : (List<?>) property.getValue()) {
233                                         traversal.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
234                                 }
235                         } else if (property.getValue() instanceof Set) {
236                                 for (Object subPropertyValue : (Set<?>) property.getValue()) {
237                                         traversal.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
238                                 }
239                         } else {
240                                 traversal.property(property.getKey(), property.getValue());
241                         }
242                 }
243
244                 return runTraversal(traversal).findFirst().get().asObject(); //TODO check if this return the updated vertices
245         }
246         
247         @Override
248         protected ChampObject doReplaceObject(ChampObject object)
249                         throws ChampMarshallingException, ChampObjectNotExistsException {
250                 //TODO: implement the replace method when required
251                 throw new UnsupportedOperationException("Method not implemented");
252         }
253         
254         @Override
255         protected ChampRelationship doReplaceRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException, ChampMarshallingException {
256                 //TODO: implement the replace method when required
257                 throw new UnsupportedOperationException("Method not implemented");
258         }
259
260         @Override
261         protected ChampRelationship doStoreRelationship(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
262
263                 /* FIXME: Only compatible with Tinkerpop 3.2.3 (Titan uses 3.0.1-incubating).
264
265                 final GraphTraversal<?, Vertex> sourceBuilder;
266
267                 if (relationship.getSource().getKey().isPresent()) {
268                         sourceBuilder = startTraversal().V(relationship.getSource().getKey().get()).as("source");
269                 } else {
270                         sourceBuilder = startTraversal().addV(relationship.getSource().getType());
271                 }
272
273                 for (Entry<String, Object> sourceProperty : relationship.getSource().getProperties().entrySet()) {
274                         sourceBuilder.property(sourceProperty.getKey(), sourceProperty.getValue());
275                 }
276
277                 final GraphTraversal<?, Vertex> targetBuilder;
278
279                 if (relationship.getTarget().getKey().isPresent()) {
280                         targetBuilder = sourceBuilder.V(relationship.getTarget().getKey().get()).as("target");
281                 } else {
282                         targetBuilder = sourceBuilder.addV(relationship.getTarget().getType());
283                 }
284
285                 for (Entry<String, Object> targetProperty : relationship.getTarget().getProperties().entrySet()) {
286                         targetBuilder.property(targetProperty.getKey(), targetProperty.getValue());
287                 }
288
289                 final GraphTraversal<?, Edge> edgeBuilder = targetBuilder.addE(relationship.getType()).from("source");
290
291                 for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
292                         edgeBuilder.property(property.getKey(), property.getValue());
293                 }
294
295                 return runTraversal(edgeBuilder).filter(e -> e.isRelationship()).findFirst().get().asRelationship();
296                 */
297
298                 throw new UnsupportedOperationException("Cannot store relationships because of project setup (Incompatible Tinkerpop version in use)");
299         }
300
301         @Override
302         protected ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampObjectNotExistsException, ChampMarshallingException, ChampRelationshipNotExistsException {
303                 if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
304
305                 try {
306                         final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
307                         final CreateChampPartitionable storedPartition = ChampPartition.create();
308
309                         for (ChampObject champObject : submittedPartition.getChampObjects()) {
310
311                                 final ChampObject objectWithKey = doStoreObject(champObject);
312                                 objectsWithKeys.put(champObject, objectWithKey);
313                         }
314
315                         for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
316                                 if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
317                                         final ChampObject objectWithKey = doStoreObject(champRelationship.getSource());
318
319                                         objectsWithKeys.put(champRelationship.getSource(), objectWithKey);
320                                 }
321
322                                 if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
323                                         final ChampObject objectWithKey = doStoreObject(champRelationship.getTarget());
324
325                                         objectsWithKeys.put(champRelationship.getTarget(), objectWithKey);
326                                 }
327
328                                 final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
329                                                                                                                                                                                         objectsWithKeys.get(champRelationship.getTarget()),
330                                                                                                                                                                                         champRelationship.getType());
331
332                                 if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
333                                 
334                                 relWithKeysBuilder.properties(champRelationship.getProperties());
335
336                                 final ChampRelationship relationship = doStoreRelationship(relWithKeysBuilder.build());
337
338                                 storedPartition.withRelationship(relationship);
339                         }
340
341                         for (ChampObject object : objectsWithKeys.values()) {
342                                 storedPartition.withObject(object);
343                         }
344
345                         return storedPartition.build();
346                 } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
347                         throw e;
348                 }
349         }
350 }