49bcfd5a7cdab2824ed375d30f2eacf557bff55d
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / serialization / db / DBSerializer.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.aai.serialization.db;
22
23
24 import java.io.UnsupportedEncodingException;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.InvocationTargetException;
27 import java.net.MalformedURLException;
28 import java.net.URI;
29 import java.net.URISyntaxException;
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.Set;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Future;
42
43 import javax.ws.rs.core.UriBuilder;
44
45 import org.apache.commons.collections.IteratorUtils;
46 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
47 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
48 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
49 import org.apache.tinkerpop.gremlin.structure.Direction;
50 import org.apache.tinkerpop.gremlin.structure.Edge;
51 import org.apache.tinkerpop.gremlin.structure.Element;
52 import org.apache.tinkerpop.gremlin.structure.Vertex;
53 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
54 import org.javatuples.Pair;
55 import org.openecomp.aai.db.props.AAIProperties;
56 import org.openecomp.aai.exceptions.AAIException;
57 import org.openecomp.aai.introspection.Introspector;
58 import org.openecomp.aai.introspection.IntrospectorFactory;
59 import org.openecomp.aai.introspection.Loader;
60 import org.openecomp.aai.introspection.LoaderFactory;
61 import org.openecomp.aai.introspection.ModelType;
62 import org.openecomp.aai.introspection.PropertyPredicates;
63 import org.openecomp.aai.introspection.Version;
64 import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException;
65 import org.openecomp.aai.introspection.sideeffect.DataCopy;
66 import org.openecomp.aai.introspection.sideeffect.DataLinkReader;
67 import org.openecomp.aai.introspection.sideeffect.DataLinkWriter;
68 import org.openecomp.aai.introspection.sideeffect.SideEffectRunner;
69 import org.openecomp.aai.logging.ErrorLogHelper;
70 import org.openecomp.aai.parsers.query.QueryParser;
71 import org.openecomp.aai.parsers.uri.URIParser;
72 import org.openecomp.aai.parsers.uri.URIToRelationshipObject;
73 import org.openecomp.aai.query.builder.QueryBuilder;
74 import org.openecomp.aai.schema.enums.ObjectMetadata;
75 import org.openecomp.aai.schema.enums.PropertyMetadata;
76 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
77 import org.openecomp.aai.serialization.engines.TransactionalGraphEngine;
78 import org.openecomp.aai.serialization.tinkerpop.TreeBackedVertex;
79 import org.openecomp.aai.util.AAIApiServerURLBase;
80 import org.openecomp.aai.util.AAIConfig;
81 import org.openecomp.aai.util.AAIConstants;
82 import org.openecomp.aai.workarounds.NamingExceptions;
83
84 import com.att.eelf.configuration.EELFLogger;
85 import com.att.eelf.configuration.EELFManager;
86 import com.google.common.base.CaseFormat;
87 import com.thinkaurelius.titan.core.SchemaViolationException;
88
89 public class DBSerializer {
90         
91         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
92         
93         private final TransactionalGraphEngine engine;
94         private final String sourceOfTruth;
95         private final ModelType introspectionType;
96         private final Version version;
97         private final Loader latestLoader;
98         private final EdgeRules edgeRules = EdgeRules.getInstance();
99         private final Loader loader;
100         private final String baseURL;
101         /**
102          * Instantiates a new DB serializer.
103          *
104          * @param version the version
105          * @param engine the engine
106          * @param g the g
107          * @param introspectionType the introspection type
108          * @param sourceOfTruth the source of truth
109          * @param llBuilder the ll builder
110          * @throws AAIException 
111          */
112         public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
113                 this.engine = engine;
114                 this.sourceOfTruth = sourceOfTruth;
115                 this.introspectionType = introspectionType;
116                 this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST);
117                 this.version = version;
118                 this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version);
119                 this.baseURL = AAIApiServerURLBase.get(version);
120         }
121         
122         /**
123          * Touch standard vertex properties.
124          *
125          * @param v the v
126          * @param isNewVertex the is new vertex
127          */
128         /*
129          * to be defined and expanded later
130          */
131         public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
132                 
133                 long unixTimeNow = System.currentTimeMillis();
134                 String timeNowInSec = "" + unixTimeNow;
135                 if (isNewVertex) {
136                         v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
137                         v.property(AAIProperties.CREATED_TS, timeNowInSec);
138                 }
139                 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
140                 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
141                 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
142                 
143         }
144         
145         private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
146
147                 v.property(AAIProperties.NODE_TYPE, nodeType);
148                 touchStandardVertexProperties(v, isNewVertex);
149
150         }
151
152         
153         
154         /**
155          * Creates the new vertex.
156          *
157          * @param wrappedObject the wrapped object
158          * @return the vertex
159          * @throws UnsupportedEncodingException the unsupported encoding exception
160          * @throws AAIException the AAI exception
161          */
162         public Vertex createNewVertex(Introspector wrappedObject) {
163
164                 
165                 Vertex v = this.engine.tx().addVertex();
166                 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
167                 
168                 return v;
169         }
170         
171         /**
172          * Trim class name.
173          *
174          * @param className the class name
175          * @return the string
176          */
177         /*
178          * Removes the classpath from a class name
179          */
180         public String trimClassName (String className) {
181                 String returnValue = "";
182                 
183                 if (className.lastIndexOf('.') == -1) {
184                         return className;
185                 }
186                 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
187                 
188                 return returnValue;
189         }
190         
191         /**
192          * Serialize to db.
193          *
194          * @param obj the obj
195          * @param v the v
196          * @param uriQuery the uri query
197          * @param identifier the identifier
198          * @throws SecurityException the security exception
199          * @throws IllegalAccessException the illegal access exception
200          * @throws IllegalArgumentException the illegal argument exception
201          * @throws InvocationTargetException the invocation target exception
202          * @throws InstantiationException the instantiation exception
203          * @throws InterruptedException the interrupted exception
204          * @throws NoSuchMethodException the no such method exception
205          * @throws AAIException the AAI exception
206          * @throws UnsupportedEncodingException the unsupported encoding exception
207          * @throws AAIUnknownObjectException 
208          */
209         public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
210
211                 try {
212                         if (uriQuery.isDependent()) {
213                                 //try to find the parent
214                                 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
215                                 if (!vertices.isEmpty()) {
216                                         Vertex parent = vertices.get(0);
217                                         this.reflectDependentVertex(parent, v, obj, requestContext);
218                                 } else {
219                                         throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
220                                 }
221                         } else {
222                                 serializeSingleVertex(v, obj, requestContext);
223                         }
224
225                 } catch (SchemaViolationException e) {
226                         throw new AAIException("AAI_6117", e);
227                 }
228                 
229         }
230         
231         public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
232                 try {
233                         boolean isTopLevel = obj.isTopLevel();
234                         if (isTopLevel) {
235                                 v.property(AAIProperties.AAI_URI, obj.getURI());
236                         }
237                         processObject(obj, v, requestContext);
238                         if (!isTopLevel) {
239                                 URI uri = this.getURIForVertex(v);
240                                 URIParser parser = new URIParser(this.loader, uri);
241                                 if (parser.validate()) {
242                                         VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
243                                         if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
244                                                 v.property(AAIProperties.AAI_URI, uri.toString());
245                                         }
246                                 }
247                         }
248                 } catch (SchemaViolationException e) {
249                         throw new AAIException("AAI_6117", e);
250                 }
251         }
252         
253         /**
254          * Process object.
255          *
256          * @param <T> the generic type
257          * @param obj the obj
258          * @param v the v
259          * @return the list
260          * @throws IllegalAccessException the illegal access exception
261          * @throws IllegalArgumentException the illegal argument exception
262          * @throws InvocationTargetException the invocation target exception
263          * @throws InstantiationException the instantiation exception
264          * @throws NoSuchMethodException the no such method exception
265          * @throws SecurityException the security exception
266          * @throws AAIException the AAI exception
267          * @throws UnsupportedEncodingException the unsupported encoding exception
268          * @throws AAIUnknownObjectException 
269          */
270         /*
271          * Helper method for reflectToDb
272          * Handles all the property setting
273          */
274         private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
275                 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
276                 properties.remove(AAIProperties.RESOURCE_VERSION);
277                 List<Vertex> dependentVertexes = new ArrayList<>();
278                 List<Vertex> processedVertexes = new ArrayList<>();
279                 boolean isComplexType = false;
280                 boolean isListType = false;
281                 if (!obj.isContainer()) {
282                         this.touchStandardVertexProperties(obj.getDbName(), v, false);
283                 }
284                 this.executePreSideEffects(obj, v);
285                 for (String property : properties) {
286                         Object value = null;
287                         final String propertyType;
288                         propertyType = obj.getType(property);
289                         isComplexType = obj.isComplexType(property);
290                         isListType = obj.isListType(property);
291                         value = obj.getValue(property);
292
293                         if (!(isComplexType || isListType)) {
294                                 boolean canModify = this.canModify(obj, property, requestContext);
295                                 
296                                 if (canModify) {
297                                         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
298                                         String dbProperty = property;
299                                         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
300                                                 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
301                                         }
302                                         if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
303                                                 //data linked properties are ephemeral
304                                                 //they are populated dynamically on GETs
305                                                 continue;
306                                         }
307                                         if (value != null) {
308                                                 if (!value.equals(v.property(dbProperty).orElse(null))) {
309                                                         if (propertyType.toLowerCase().contains(".long")) {
310                                                                 v.property(dbProperty, new Integer(((Long)value).toString()));
311                                                         } else {
312                                                                 v.property(dbProperty, value);
313                                                         }
314                                                 }
315                                         } else {
316                                                 v.property(dbProperty).remove();
317                                         }
318                                 }
319                         } else if (isListType) {
320                                 List<Object> list = (List<Object>)value;
321                                 if (obj.isComplexGenericType(property)) {
322                                         if (list != null) {
323                                                 for (Object o : list) {
324                                                         Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
325                                                         child.setURIChain(obj.getURI());
326                                                         processedVertexes.add(reflectDependentVertex(v, child, requestContext));
327                                                 }
328                                         }
329                                 } else {
330                                         //simple list case
331                                         engine.setListProperty(v, property, list);
332                                 }
333                         } else {
334                                 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
335                                 if (value != null) { //effectively ignore complex properties not included in the object we're processing
336                                         if (value.getClass().isArray()) {
337                                                 
338                                                 int length = Array.getLength(value);
339                                             for (int i = 0; i < length; i ++) {
340                                                 Object arrayElement = Array.get(value, i);
341                                                 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
342                                                         child.setURIChain(obj.getURI());
343                                                         processedVertexes.add(reflectDependentVertex(v, child, requestContext));
344
345                                             }
346                                         } else if (!property.equals("relationship-list")) {
347                                                 // container case
348                                                 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
349                                                 if (introspector.isContainer()) {
350                                                         dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
351                                                         introspector.setURIChain(obj.getURI());
352                                                         
353                                                         processedVertexes.addAll(processObject(introspector, v, requestContext));
354
355                                                 } else {
356                                                         dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
357                                                         processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
358
359                                                 }
360                                         } else if (property.equals("relationship-list")) {
361                                                 handleRelationships(obj, v);
362                                         }
363                                 }
364                         }
365                 }
366                 this.writeThroughDefaults(v, obj);
367                 /* handle those vertexes not touched */
368                 for (Vertex toBeRemoved : processedVertexes) {
369                         dependentVertexes.remove(toBeRemoved);
370                 }
371                 this.deleteItemsWithTraversal(dependentVertexes);
372                 
373                 this.executePostSideEffects(obj, v);
374                 return processedVertexes;
375         }
376         
377         /**
378          * Handle relationships.
379          *
380          * @param obj the obj
381          * @param vertex the vertex
382          * @throws SecurityException the security exception
383          * @throws IllegalAccessException the illegal access exception
384          * @throws IllegalArgumentException the illegal argument exception
385          * @throws InvocationTargetException the invocation target exception
386          * @throws UnsupportedEncodingException the unsupported encoding exception
387          * @throws AAIException the AAI exception
388          */
389         /*
390          * Handles the explicit relationships defined for an obj
391          */
392         private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
393                 
394         
395         
396                 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
397                 processRelationshipList(wrappedRl, vertex);
398                 
399         
400         }
401         
402         
403         /**
404          * Process relationship list.
405          *
406          * @param wrapped the wrapped
407          * @param v the v
408          * @throws UnsupportedEncodingException the unsupported encoding exception
409          * @throws AAIException the AAI exception
410          */
411         private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
412                                 
413                 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
414                 
415                 List<Pair<Vertex, Vertex>> addEdges = new ArrayList<>();
416                 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
417                 
418                 for (Object relationship : relationships) {
419                         Edge e = null;
420                         Vertex cousinVertex = null;
421                         Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
422                         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
423                         
424                         List<Vertex> results = parser.getQueryBuilder().toList();
425                         if (results.isEmpty()) {
426                                 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
427                                 ex.getTemplateVars().add(parser.getResultType());
428                                 ex.getTemplateVars().add(parser.getUri().toString());
429                                 throw ex;
430                         } else { 
431                                 //still an issue if there's more than one
432                                 cousinVertex = results.get(0);
433                         }
434                         
435                         if (cousinVertex != null) {
436                                 try {
437                                         e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex);
438
439                                         if (e == null) {
440                                                 addEdges.add(new Pair<>(v, cousinVertex));
441                                         } else { 
442                                                 existingEdges.remove(e);
443                                         }
444                                 } catch (NoEdgeRuleFoundException e1) {
445                                         throw new AAIException("AAI_6145");
446                                 }
447                         }
448                 }
449                 
450                 for (Edge edge : existingEdges) {
451                         edge.remove();
452                 }
453                 for (Pair<Vertex, Vertex> pair : addEdges) {
454                          try {
455                                 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1());
456                         } catch (NoEdgeRuleFoundException e) {
457                                 throw new AAIException("AAI_6129", e);
458                         }                       
459                 }
460
461         }
462
463         /**
464          * Write through defaults.
465          *
466          * @param v the v
467          * @param obj the obj
468          * @throws AAIUnknownObjectException 
469          */
470         private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
471                 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
472                 if (latest != null) {
473                         Set<String> required  = latest.getRequiredProperties();
474                         
475                         for (String field : required) {
476                                 String defaultValue = null;
477                                 Object vertexProp = null;
478                                 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
479                                 if (defaultValue != null) {
480                                         vertexProp = v.<Object>property(field).orElse(null);
481                                         if (vertexProp == null) {
482                                                 v.property(field, defaultValue);
483                                         }
484                                 }
485                         }
486                 }
487                 
488         }
489
490         
491         /**
492           * Reflect dependent vertex.
493           *
494           * @param v the v
495           * @param dependentObj the dependent obj
496           * @return the vertex
497           * @throws IllegalAccessException the illegal access exception
498           * @throws IllegalArgumentException the illegal argument exception
499           * @throws InvocationTargetException the invocation target exception
500           * @throws InstantiationException the instantiation exception
501           * @throws NoSuchMethodException the no such method exception
502           * @throws SecurityException the security exception
503           * @throws AAIException the AAI exception
504           * @throws UnsupportedEncodingException the unsupported encoding exception
505          * @throws AAIUnknownObjectException 
506           */
507          private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
508                 
509                 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
510                 //List<Vertex> items = p.getQuery().toList();
511                 QueryBuilder query = this.engine.getQueryBuilder(v);
512                 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
513                 query.createKeyQuery(dependentObj);
514                 
515                 List<Vertex> items = query.toList();
516                 
517                 Vertex dependentVertex = null;
518                 if (items.size() == 1) {
519                         dependentVertex = items.get(0);
520                         this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
521                 } else {
522                         this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
523                         dependentVertex = createNewVertex(dependentObj);
524                 }
525
526                 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
527                                 
528         }
529         
530         /**
531           * Reflect dependent vertex.
532           *
533           * @param parent the parent
534           * @param child the child
535           * @param obj the obj
536           * @return the vertex
537           * @throws IllegalAccessException the illegal access exception
538           * @throws IllegalArgumentException the illegal argument exception
539           * @throws InvocationTargetException the invocation target exception
540           * @throws InstantiationException the instantiation exception
541           * @throws NoSuchMethodException the no such method exception
542           * @throws SecurityException the security exception
543           * @throws AAIException the AAI exception
544           * @throws UnsupportedEncodingException the unsupported encoding exception
545          * @throws AAIUnknownObjectException 
546           */
547          private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
548                 
549                 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
550                 if (parentUri != null) {
551                         String uri;
552                         uri = obj.getURI();
553                         child.property(AAIProperties.AAI_URI, parentUri + uri);
554                 }
555                 processObject(obj, child, requestContext);
556                 
557                 Edge e;
558                 e = this.getEdgeBetween(EdgeType.TREE, parent, child);
559                 if (e == null) {
560                         String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
561                         if (canBeLinked != null && canBeLinked.equals("true")) {
562                                 boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
563                                 if (isFirst) {
564                                         child.property(AAIProperties.LINKED, true);
565                                 }
566                         }
567                         edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
568                 }
569                 return child;
570                 
571         }
572          
573         /**
574           * Db to object.
575           *
576           * @param vertices the vertices
577           * @param obj the obj
578           * @param depth the depth
579           * @param cleanUp the clean up
580           * @return the introspector
581           * @throws AAIException the AAI exception
582           * @throws IllegalAccessException the illegal access exception
583           * @throws IllegalArgumentException the illegal argument exception
584           * @throws InvocationTargetException the invocation target exception
585           * @throws SecurityException the security exception
586           * @throws InstantiationException the instantiation exception
587           * @throws NoSuchMethodException the no such method exception
588           * @throws UnsupportedEncodingException the unsupported encoding exception
589           * @throws MalformedURLException the malformed URL exception
590          * @throws AAIUnknownObjectException 
591          * @throws URISyntaxException 
592           */
593          public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
594                 
595                 final int internalDepth;
596                 if (depth == Integer.MAX_VALUE) {
597                         internalDepth = depth--;
598                 } else {
599                         internalDepth = depth;
600                 }
601                 if (vertices.size() > 1 && !obj.isContainer()) {
602                         throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
603                 } else if (obj.isContainer()) {
604                         final List getList;
605                         String listProperty = null;
606                         for (String property : obj.getProperties()) {
607                                 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
608                                         listProperty = property;
609                                         break;
610                                 }
611                         }
612                         final String propertyName = listProperty;
613                         getList = (List)obj.getValue(listProperty);
614                         
615                         /* This is an experimental multithreading experiment
616                          * on get alls. 
617                          */
618                         ExecutorService pool = GetAllPool.getInstance().getPool();
619                         
620                         List<Future<Object>> futures = new ArrayList<>();
621                         for (Vertex v : vertices) {
622                                 Callable<Object> task = () -> {
623                                         Set<Vertex> seen = new HashSet<>();
624                                         Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
625                                         Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
626                                         TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
627                                         dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
628                                         return childObject.getUnderlyingObject();
629                                         //getList.add(childObject.getUnderlyingObject());
630                                 };
631                                 futures.add(pool.submit(task));
632                         }
633                         
634                         for (Future<Object> future : futures) {
635                                 try {
636                                         getList.add(future.get());
637                                 } catch (ExecutionException e) {
638                                         throw new AAIException("AAI_4000", e);
639                                 } catch (InterruptedException e) {
640                                         throw new AAIException("AAI_4000", e);
641                                 }
642                         }
643                 } else if (vertices.size() == 1) {
644                         Set<Vertex> seen = new HashSet<>();
645                         Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
646                         TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
647                         dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
648                 } else {
649                         //obj = null;
650                 }
651                 
652
653                 return obj;
654         }
655         
656         /**
657          * Db to object.
658          *
659          * @param obj the obj
660          * @param v the v
661          * @param seen the seen
662          * @param depth the depth
663          * @param cleanUp the clean up
664          * @return the introspector
665          * @throws IllegalAccessException the illegal access exception
666          * @throws IllegalArgumentException the illegal argument exception
667          * @throws InvocationTargetException the invocation target exception
668          * @throws SecurityException the security exception
669          * @throws InstantiationException the instantiation exception
670          * @throws NoSuchMethodException the no such method exception
671          * @throws UnsupportedEncodingException the unsupported encoding exception
672          * @throws AAIException the AAI exception
673          * @throws MalformedURLException the malformed URL exception
674          * @throws AAIUnknownObjectException 
675          * @throws URISyntaxException 
676          */
677         private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
678                 
679                 if (depth < 0) {
680                         return null;
681                 }
682                 depth--;
683                 seen.add(v);
684                 
685                 boolean modified = false;
686                 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
687                         List<Object> getList = null;
688                         Vertex[] vertices = null;
689
690                         if (!(obj.isComplexType(property) || obj.isListType(property))) {
691                                 this.copySimpleProperty(property, obj, v);
692                                 modified = true;
693                         } else {
694                                 if (obj.isComplexType(property)) {
695                                 /* container case */
696         
697                                         if (!property.equals("relationship-list") && depth >= 0) {
698                                                 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
699                                                 Object result  = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
700                                                 if (result != null) {
701                                                         obj.setValue(property, argumentObject.getUnderlyingObject());
702                                                         modified = true;
703                                                 }
704                                         } else if (property.equals("relationship-list") && !nodeOnly){
705                                                 /* relationships need to be handled correctly */
706                                                 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
707                                                 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
708                                                 if (relationshipList != null) {
709                                                         modified = true;
710                                                         obj.setValue(property, relationshipList.getUnderlyingObject());
711                                                         modified = true;
712                                                 }
713                                                 
714                                         }
715                                 } else if (obj.isListType(property)) {
716                                         
717                                         if (property.equals("any")) {
718                                                 continue;
719                                         }
720                                         String genericType = obj.getGenericTypeClass(property).getSimpleName();
721                                         if (obj.isComplexGenericType(property) && depth >= 0) {
722                                                 final String childDbName = convertFromCamelCase(genericType);
723                                                 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
724                                                 EdgeRule rule;
725                                                 
726                                                 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
727                                                 if (rule.getIsParent().equals("true") || rule.getIsParent().equals("reverse")) {
728                                                         //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
729                                                         Direction ruleDirection = rule.getDirection();
730                                                         Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
731                                                         List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
732                                                         itr = verticesList.stream().filter(item -> {
733                                                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
734                                                         }).iterator();
735                                                         if (itr.hasNext()) {
736                                                                 getList = (List<Object>)obj.getValue(property);
737                                                         }
738                                                         int processed = 0;
739                                                         int removed = 0;
740                                                         while (itr.hasNext()) {
741                                                                 Vertex childVertex = itr.next();
742                                                                 if (!seen.contains(childVertex)) {
743                                                                         Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
744                                                                         
745                                                                         Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
746                                                                         if (result != null) { 
747                                                                                 getList.add(argumentObject.getUnderlyingObject());
748                                                                         }
749                                                                         
750                                                                         processed++;
751                                                                 } else {
752                                                                         removed++;
753                                                                         LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
754                                                                 }
755                                                         }
756                                                         if (processed == 0) {
757                                                                 //vertices were all seen, reset the list
758                                                                 getList = null;
759                                                         }
760                                                         if (processed > 0) {
761                                                                 modified = true;
762                                                         }
763                                                 }
764                                         } else if (obj.isSimpleGenericType(property)) {
765                                                 List<Object> temp = this.engine.getListProperty(v, property);
766                                                 if (temp != null) {
767                                                         getList = (List<Object>)obj.getValue(property);
768                                                         getList.addAll(temp);
769                                                         modified = true;
770                                                 }
771
772                                         }
773
774                                 }
775
776                         }
777                 }
778                 
779                 //no changes were made to this obj, discard the instance
780                 if (!modified) {
781                         return null;
782                 }
783                 this.enrichData(obj, v);
784                 return obj;
785                 
786         }
787         
788         
789         public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
790                 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
791                 if (nodeType == null) {
792                         throw new AAIException("AAI_6143");
793                 }
794                 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
795                 Set<Vertex> seen = new HashSet<>();
796                 int depth = 0;
797                 String cleanUp = "false";
798                 boolean nodeOnly = true;
799                 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
800                 
801                 return obj;
802                 
803         }
804         public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
805                 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
806                 if (nodeType == null) {
807                         throw new AAIException("AAI_6143");
808                 }
809                 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
810                 Set<Vertex> seen = new HashSet<>();
811                 int depth = AAIProperties.MAXIMUM_DEPTH;
812                 String cleanUp = "false";
813                 boolean nodeOnly = false;
814                 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
815                 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
816                 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
817                 
818                 return obj;
819         }
820         /**
821          * Copy simple property.
822          *
823          * @param property the property
824          * @param obj the obj
825          * @param v the v
826          * @throws InstantiationException the instantiation exception
827          * @throws IllegalAccessException the illegal access exception
828          * @throws IllegalArgumentException the illegal argument exception
829          * @throws InvocationTargetException the invocation target exception
830          * @throws NoSuchMethodException the no such method exception
831          * @throws SecurityException the security exception
832          */
833         private void copySimpleProperty(String property, Introspector obj, Vertex v) {
834                 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
835                 String dbPropertyName = property;
836                 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
837                         dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
838                 }
839                 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
840                 if (temp != null) {
841                         
842                         obj.setValue(property, temp);
843                 }
844         }
845         
846         /**
847          * Simple db to object.
848          *
849          * @param obj the obj
850          * @param v the v
851          * @throws InstantiationException the instantiation exception
852          * @throws IllegalAccessException the illegal access exception
853          * @throws IllegalArgumentException the illegal argument exception
854          * @throws InvocationTargetException the invocation target exception
855          * @throws NoSuchMethodException the no such method exception
856          * @throws SecurityException the security exception
857          */
858         private void simpleDbToObject (Introspector obj, Vertex v) {
859                 for (String property : obj.getProperties()) {
860                         
861
862                         if (!(obj.isComplexType(property) || obj.isListType(property))) {
863                                 this.copySimpleProperty(property, obj, v);
864                         }
865                 }
866         }
867         
868         /**
869          * Creates the relationship list.
870          *
871          * @param v the v
872          * @param obj the obj
873          * @param cleanUp the clean up
874          * @return the object
875          * @throws InstantiationException the instantiation exception
876          * @throws IllegalAccessException the illegal access exception
877          * @throws IllegalArgumentException the illegal argument exception
878          * @throws InvocationTargetException the invocation target exception
879          * @throws NoSuchMethodException the no such method exception
880          * @throws SecurityException the security exception
881          * @throws UnsupportedEncodingException the unsupported encoding exception
882          * @throws AAIException the AAI exception
883          * @throws MalformedURLException the malformed URL exception
884          * @throws URISyntaxException 
885          */
886         private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
887                 
888
889                 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
890
891                 List<Object> relationshipObjList = obj.getValue("relationship");
892                 
893                 for (Vertex cousin : cousins) {
894                 
895                                 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
896                         Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp);
897                                 if (result != null) {
898                                         relationshipObjList.add(result);
899                                 }
900                 
901                 }
902                 
903                 if (relationshipObjList.isEmpty()) {
904                         return null;
905                 } else {
906                         return obj;
907                 }
908         }
909         
910         /**
911          * Process edge relationship.
912          *
913          * @param relationshipObj the relationship obj
914          * @param edge the edge
915          * @param direction the direction
916          * @param cleanUp the clean up
917          * @return the object
918          * @throws InstantiationException the instantiation exception
919          * @throws IllegalAccessException the illegal access exception
920          * @throws IllegalArgumentException the illegal argument exception
921          * @throws InvocationTargetException the invocation target exception
922          * @throws NoSuchMethodException the no such method exception
923          * @throws SecurityException the security exception
924          * @throws UnsupportedEncodingException the unsupported encoding exception
925          * @throws AAIException the AAI exception
926          * @throws MalformedURLException the malformed URL exception
927          * @throws AAIUnknownObjectException 
928          * @throws URISyntaxException 
929          */
930                 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException {
931
932
933                 //we must look up all parents in this case because we need to compute name-properties
934                 //we cannot used the cached aaiUri to perform this action currently
935                 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
936                 //damaged vertex found, ignore
937                 if (!tuple.isPresent()) {
938                         return null;
939                 }
940                 List<Introspector> list = tuple.get().getValue1();
941                 URI uri = this.getURIFromList(list);
942                 
943                 URIToRelationshipObject uriParser = null;
944                 Introspector result = null;
945                 try {
946                         uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
947                         result = uriParser.getResult();
948                 } catch (AAIException | URISyntaxException e) {
949                         LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " + e.getMessage(), e);
950                         if ("true".equals(cleanUp)) {
951                                 this.deleteWithTraversal(tuple.get().getValue0());
952                         }
953                         return null;
954                 }
955
956         if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){
957             this.addRelatedToProperty(result, list.get(0));
958         }
959
960                 return result.getUnderlyingObject();
961         }
962         
963         /**
964          * Gets the URI for vertex.
965          *
966          * @param v the v
967          * @return the URI for vertex
968          * @throws InstantiationException the instantiation exception
969          * @throws IllegalAccessException the illegal access exception
970          * @throws IllegalArgumentException the illegal argument exception
971          * @throws InvocationTargetException the invocation target exception
972          * @throws NoSuchMethodException the no such method exception
973          * @throws SecurityException the security exception
974          * @throws UnsupportedEncodingException the unsupported encoding exception
975          * @throws AAIUnknownObjectException 
976          */
977         public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
978                 
979                 return getURIForVertex(v, false); 
980         }
981         
982         public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
983                 URI uri = UriBuilder.fromPath("/unknown-uri").build();
984                 
985                 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
986                 
987                 if (aaiUri != null && !overwrite) {
988                         uri = UriBuilder.fromPath(aaiUri).build();
989                 } else {
990                         Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false);
991                         if (tuple.isPresent()) {
992                                 List<Introspector> list = tuple.get().getValue1();
993                                 uri = this.getURIFromList(list);
994                         }
995                 }
996                 return uri;
997         }
998         /**
999          * Gets the URI from list.
1000          *
1001          * @param list the list
1002          * @return the URI from list
1003          * @throws UnsupportedEncodingException the unsupported encoding exception
1004          */
1005         private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1006                 String uri = "";
1007                 StringBuilder sb = new StringBuilder();
1008                 for (Introspector i : list) {
1009                         sb.insert(0, i.getURI());
1010                 }
1011                 
1012                 uri = sb.toString();
1013                 URI result = UriBuilder.fromPath(uri).build();
1014                 return result;
1015         }
1016         
1017         /**
1018          * Gets the parents.
1019          *
1020          * @param start the start
1021          * @param removeDamaged the remove damaged
1022          * @return the parents
1023          * @throws InstantiationException the instantiation exception
1024          * @throws IllegalAccessException the illegal access exception
1025          * @throws IllegalArgumentException the illegal argument exception
1026          * @throws InvocationTargetException the invocation target exception
1027          * @throws NoSuchMethodException the no such method exception
1028          * @throws SecurityException the security exception
1029          * @throws AAIUnknownObjectException 
1030          */
1031         private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1032                 
1033                 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1034                 List<Introspector> objs = new ArrayList<>();
1035                 boolean shortCircuit = false;
1036                 for (Vertex v : results) {
1037                         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1038                         Introspector obj = null;
1039                         //vertex on the other end of this edge is bad
1040                         if (nodeType == null) {
1041                                 //log something here about what was found and that it was removed
1042                                 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1043                                 if (removeDamaged) {
1044                                         this.deleteWithTraversal(v);
1045                                 }
1046                                 shortCircuit = true;
1047                         } else {
1048                                 try {
1049                                         obj = loader.introspectorFromName(nodeType);
1050                                 } catch (AAIUnknownObjectException e) {
1051                                         LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1052                                         obj = null;
1053                                 }
1054                         }
1055                         
1056                         if (obj == null) {
1057                                 //can't make a valid path because we don't understand this object
1058                                 // don't include it
1059                         } else {
1060                                 this.simpleDbToObject(obj, v);
1061                                 objs.add(obj);
1062                         }
1063                 }
1064                 
1065                 //stop processing and don't return anything for this bad vertex
1066                 if (shortCircuit) {
1067                         return Optional.empty();
1068                 }
1069                 
1070                 return Optional.of(new Pair<>(results.get(results.size()-1), objs));
1071         }
1072         /**
1073          * Takes a list of vertices and a list of objs and assumes they are in
1074          * the order you want the URIs to be nested.
1075          * [A,B,C] creates uris [A, AB, ABC]
1076          * @param vertices
1077          * @param objs
1078          * @throws UnsupportedEncodingException
1079          * @throws URISyntaxException
1080          */
1081         public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1082                 
1083                 String uriChain = "";
1084                 for (int i = 0; i < vertices.size(); i++) {
1085                         String aaiUri = "";
1086                         Vertex v = null;
1087                         v = vertices.get(i);
1088                         aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1089                         if (aaiUri != null) {
1090                                 uriChain += aaiUri;
1091                         } else {
1092                                 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1093                                 aaiUri = uri.toString();
1094                                 uriChain += aaiUri;
1095                                 v.property(AAIProperties.AAI_URI, uriChain);
1096                         }
1097                 }
1098                 
1099                 
1100                 
1101         }
1102         
1103         
1104         /**
1105          * Adds the r
1106          * @throws AAIUnknownObjectException 
1107          * @throws IllegalArgumentException elated to property.
1108          *
1109          * @param relationship the relationship
1110          * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1111          */
1112         public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1113                 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1114                 List<Introspector> relatedToProperties = new ArrayList<>();
1115                 
1116                 if (nameProps != null) {
1117                         String[] props = nameProps.split(",");
1118                         for (String prop : props) {
1119                                 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1120                                 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1121                                 relatedTo.setValue("property-value", child.getValue(prop));
1122                                 relatedToProperties.add(relatedTo);
1123                         }
1124                 }
1125                 
1126                 if (relatedToProperties.size() > 0) {
1127                         List relatedToList = (List)relationship.getValue("related-to-property");
1128                         for (Introspector obj : relatedToProperties) {
1129                                 relatedToList.add(obj.getUnderlyingObject());
1130                         }
1131                 }
1132                 
1133         }
1134         
1135         /**
1136          * Creates the edge.
1137          *
1138          * @param relationship the relationship
1139          * @param inputVertex the input vertex
1140          * @return true, if successful
1141          * @throws UnsupportedEncodingException the unsupported encoding exception
1142          * @throws AAIException the AAI exception
1143          */
1144         public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1145                 
1146                 Vertex relatedVertex = null;
1147                 
1148                 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1149                 
1150                 List<Vertex> results = parser.getQueryBuilder().toList();
1151                 if (results.size() == 0) {
1152                         AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1153                         e.getTemplateVars().add(parser.getResultType());
1154                         e.getTemplateVars().add(parser.getUri().toString());
1155                         throw e;
1156                 } else { 
1157                         //still an issue if there's more than one
1158                         relatedVertex = results.get(0);
1159                 }
1160
1161                 if (relatedVertex != null) {
1162
1163                         Edge e;
1164                         try {
1165                                 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1166                                 if (e == null) {                                
1167                                         edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex);
1168
1169                                 } else {
1170                                         //attempted to link two vertexes already linked
1171                                 }
1172                         } catch (NoEdgeRuleFoundException e1) {
1173                                 throw new AAIException("AAI_6129", e1);
1174                         }
1175                         
1176                         
1177                         
1178
1179                 }
1180                 
1181                 return true;
1182         }
1183         
1184         /**
1185          * Gets the edges between.
1186          *
1187          * @param aVertex the out vertex
1188          * @param bVertex the in vertex
1189          * @return the edges between
1190          * @throws AAIException the AAI exception
1191          * @throws NoEdgeRuleFoundException 
1192          */
1193         private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException {
1194                 
1195                 List<Edge> result = new ArrayList<>();
1196                 
1197                 if (bVertex != null) {
1198                                 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex);
1199                                 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1200                                 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id()));
1201                                 List<Edge> edges = findEdgesBetween.toList();
1202                                 for (Edge edge : edges) {
1203                                         if (edge.label().equals(rule.getLabel())) {
1204                                                 result.add(edge);
1205                                         }
1206                                 }
1207
1208                 }
1209                 
1210                 return result;
1211         }
1212         
1213         /**
1214          * Gets the edge between.
1215          *
1216          * @param aVertex the out vertex
1217          * @param bVertex the in vertex
1218          * @return the edge between
1219          * @throws AAIException the AAI exception
1220          * @throws NoEdgeRuleFoundException 
1221          */
1222         public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1223                 
1224                 
1225                 
1226                 if (bVertex != null) {
1227
1228                                 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1229                                 
1230                                 if (edges.size() > 0) {
1231                                         return edges.get(0);
1232                                 }
1233
1234                 }
1235                 
1236                 return null;
1237         }
1238         
1239
1240         /**
1241          * Delete edge.
1242          *
1243          * @param relationship the relationship
1244          * @param inputVertex the input vertex
1245          * @return true, if successful
1246          * @throws UnsupportedEncodingException the unsupported encoding exception
1247          * @throws AAIException the AAI exception
1248          */
1249         public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1250                 
1251                 Vertex relatedVertex = null;
1252
1253                 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1254                 
1255                 List<Vertex> results = parser.getQueryBuilder().toList();
1256
1257                 if (results.size() == 0) {
1258                         return false;
1259                 }
1260                 
1261                 relatedVertex = results.get(0);
1262                 Edge edge;
1263                 try {
1264                         edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1265                 } catch (NoEdgeRuleFoundException e) {
1266                         throw new AAIException("AAI_6129", e);
1267                 }
1268                 if (edge != null) {
1269                         edge.remove();
1270                         return true;
1271                 } else {
1272                         return false;
1273                 }
1274                 
1275         }
1276         
1277         /**
1278          * Delete items with traversal.
1279          *
1280          * @param vertexes the vertexes
1281          * @throws IllegalStateException the illegal state exception
1282          */
1283         public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1284                 for (Vertex v : vertexes) {
1285                         LOGGER.debug("About to delete the vertex with id: " + v.id());
1286                         deleteWithTraversal(v);
1287                 }
1288         }
1289         
1290         /**
1291          * Delete with traversal.
1292          *
1293          * @param startVertex the start vertex
1294          */
1295         public void deleteWithTraversal(Vertex startVertex) {
1296                 
1297                 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1298                 
1299                 for (Vertex v : results) {
1300                         LOGGER.warn("Removing vertex " + v.id().toString());
1301
1302                         v.remove();
1303                 }
1304                 
1305         }
1306
1307         /**
1308          * Delete.
1309          *
1310          * @param v the v
1311          * @param resourceVersion the resource version
1312          * @throws IllegalArgumentException the illegal argument exception
1313          * @throws AAIException the AAI exception
1314          * @throws InterruptedException the interrupted exception
1315          */
1316         public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1317         
1318                 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1319                 if (result) {
1320                         try {
1321                                 deleteWithTraversal(v);
1322                         } catch (IllegalStateException e) {
1323                                 throw new AAIException("AAI_6110", e);
1324                         }
1325
1326                 }
1327                 
1328         }
1329         
1330
1331         /**
1332          * Verify delete semantics.
1333          *
1334          * @param vertex the vertex
1335          * @param resourceVersion the resource version
1336          * @return true, if successful
1337          * @throws AAIException the AAI exception
1338          */
1339         private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1340                 boolean result = false;
1341                 String nodeType = "";
1342                 DeleteSemantic semantic = null;
1343                 List<Edge> inEdges = null;
1344                 List<Edge> outEdges = null;
1345                 String errorDetail = " unknown delete semantic found";
1346                 String aaiExceptionCode = "";
1347                 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1348                 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1349                 }
1350                 semantic = edgeRules.getDeleteSemantic(nodeType);
1351                 inEdges = (List<Edge>)IteratorUtils.toList(vertex.edges(Direction.IN));
1352                 outEdges = (List<Edge>)IteratorUtils.toList(vertex.edges(Direction.OUT));
1353                 if (semantic.equals(DeleteSemantic.CASCADE_TO_CHILDREN)) {
1354                         result = true;
1355                 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_EDGES)) {
1356                         if (inEdges.isEmpty() && outEdges.isEmpty()) {
1357                                 result = true;
1358                         } else {
1359                                 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1360                                 aaiExceptionCode = "AAI_6110";
1361                         }
1362                 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_IN_EDGES) || semantic.equals(DeleteSemantic.ERROR_4_IN_EDGES_OR_CASCADE)) {
1363                         
1364                         if (inEdges.isEmpty()) {
1365                                 result = true;
1366                         } else {
1367                                 //are there any cousin edges?
1368                                 long children = 0;
1369                                 for (Edge e : inEdges) {
1370                                         if (e.<Boolean>property(EdgeProperties.out(EdgeProperty.IS_PARENT)).orElse(false)) {
1371                                                 children++;
1372                                         }
1373                                 }
1374                                 for (Edge e : outEdges) {
1375                                         if (e.<Boolean>property(EdgeProperties.in(EdgeProperty.IS_PARENT)).orElse(false)) {
1376                                                 children++;
1377                                         }
1378                                 }
1379                                 
1380                                 result = children == inEdges.size();
1381                         }
1382                         
1383                         if (!result) {
1384                                 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1385                                 aaiExceptionCode = "AAI_6110";
1386                         }
1387                 } else if (semantic.equals(DeleteSemantic.THIS_NODE_ONLY)) {
1388                         if (outEdges.isEmpty() && inEdges.isEmpty()) {
1389                                 result = true;
1390                         } else {
1391                                 result = true;
1392                                 for (Edge edge : outEdges) {
1393                                         Object property = edge.<Boolean>property(EdgeProperties.out(EdgeProperty.IS_PARENT)).orElse(null);
1394                                         if (property != null && property.equals(Boolean.TRUE)) {
1395                                                 Vertex v = edge.inVertex();
1396                                                 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1397                                                 errorDetail = " Node cannot be deleted using scope = " + semantic + 
1398                                                                 " another node (type = " + vType + ") depends on it for uniqueness.";
1399                                                 aaiExceptionCode = "AAI_6110";
1400                                                 result = false;
1401                                                 break;
1402                                         }
1403                                 }
1404                                 
1405                                 for (Edge edge : inEdges) {
1406                                         Object property = edge.<Boolean>property(EdgeProperties.in(EdgeProperty.IS_PARENT)).orElse(null);
1407                                         if (property != null && property.equals(Boolean.TRUE)) {
1408                                                 Vertex v = edge.outVertex();
1409                                                 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1410                                                 errorDetail = " Node cannot be deleted using scope = " + semantic + 
1411                                                                 " another node (type = " + vType + ") depends on it for uniqueness.";
1412                                                 aaiExceptionCode = "AAI_6110";
1413                                                 result = false;
1414                                                 break;
1415                                         }
1416                                 }
1417                         }
1418                 }
1419                 
1420                 
1421                 if (!result) {
1422                         throw new AAIException(aaiExceptionCode, errorDetail); 
1423                 }
1424                 return result;
1425         }
1426
1427         /**
1428          * Verify resource version.
1429          *
1430          * @param action the action
1431          * @param nodeType the node type
1432          * @param currentResourceVersion the current resource version
1433          * @param resourceVersion the resource version
1434          * @param uri the uri
1435          * @return true, if successful
1436          * @throws AAIException the AAI exception
1437          */
1438         public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1439                 String enabled = "";
1440                 String errorDetail = "";
1441                 String aaiExceptionCode = "";
1442                 if (currentResourceVersion == null) {
1443                         currentResourceVersion = "";
1444                 }
1445                 
1446                 if (resourceVersion == null) {
1447                         resourceVersion = "";
1448                 }
1449                 try {
1450                         enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1451                 } catch (AAIException e) {
1452                         ErrorLogHelper.logException(e);
1453                 }
1454                 // We're only doing the resource version checks for v5 and later
1455                 if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) {
1456                         if (!currentResourceVersion.equals(resourceVersion)) {
1457                                 if (action.equals("create") && !resourceVersion.equals("")) {
1458                                         errorDetail = "resource-version passed for " + action + " of " + uri;
1459                                         aaiExceptionCode = "AAI_6135";
1460                                 } else if (resourceVersion.equals("")) {
1461                                         errorDetail = "resource-version not passed for " + action + " of " + uri;
1462                                         aaiExceptionCode = "AAI_6130";
1463                                 } else {
1464                                         errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1465                                         aaiExceptionCode = "AAI_6131";
1466                                 }
1467                                 
1468                                 throw new AAIException(aaiExceptionCode, errorDetail); 
1469                                 
1470                         }
1471                 }
1472                 return true;
1473         }
1474         
1475         /**
1476          * Convert from camel case.
1477          *
1478          * @param name the name
1479          * @return the string
1480          */
1481         private String convertFromCamelCase (String name) {
1482                 String result = "";
1483                 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1484                 
1485                 NamingExceptions exceptions = NamingExceptions.getInstance();
1486                 result = exceptions.getDBName(result);
1487                 
1488                 return result;
1489         }
1490         
1491         private boolean canModify(Introspector obj, String propName, String requestContext) {
1492                 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1493                 if (readOnly != null) {
1494                         final String[] items = readOnly.split(",");
1495                         for (String item : items) {
1496                                 if (requestContext.equals(item)) {
1497                                         return false;
1498                                 }
1499                         }
1500                 }
1501                 return true;
1502         }
1503         
1504         private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1505                 
1506                 SideEffectRunner runner = new SideEffectRunner
1507                                 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1508                 
1509                 runner.execute(obj, self);
1510         }
1511         
1512         private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1513                 
1514                 SideEffectRunner runner = new SideEffectRunner
1515                                 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1516                 
1517                 runner.execute(obj, self);
1518         }
1519         
1520         private void enrichData(Introspector obj, Vertex self) throws AAIException  {
1521                 
1522                 SideEffectRunner runner = new SideEffectRunner
1523                                 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1524                 
1525                 runner.execute(obj, self);
1526         }
1527
1528 }