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