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