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