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