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