Fix a potential bug in DBSerializer
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / serialization / db / DBSerializer.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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 package org.onap.aai.serialization.db;
21
22
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.google.common.base.CaseFormat;
26 import com.google.common.collect.Multimap;
27 import org.apache.commons.collections.IteratorUtils;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
30 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
31 import org.apache.tinkerpop.gremlin.structure.*;
32 import org.janusgraph.core.SchemaViolationException;
33 import org.javatuples.Triplet;
34 import org.onap.aai.concurrent.AaiCallable;
35 import org.onap.aai.config.SpringContextAware;
36 import org.onap.aai.db.props.AAIProperties;
37 import org.onap.aai.edges.EdgeIngestor;
38 import org.onap.aai.edges.EdgeRule;
39 import org.onap.aai.edges.EdgeRuleQuery;
40 import org.onap.aai.edges.TypeAlphabetizer;
41 import org.onap.aai.edges.enums.AAIDirection;
42 import org.onap.aai.edges.enums.EdgeField;
43 import org.onap.aai.edges.enums.EdgeProperty;
44 import org.onap.aai.edges.enums.EdgeType;
45 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
46 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.introspection.*;
49 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
50 import org.onap.aai.introspection.sideeffect.*;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.logging.LogFormatTools;
53 import org.onap.aai.logging.LoggingContext;
54 import org.onap.aai.logging.StopWatch;
55 import org.onap.aai.parsers.query.QueryParser;
56 import org.onap.aai.parsers.uri.URIParser;
57 import org.onap.aai.parsers.uri.URIToRelationshipObject;
58 import org.onap.aai.query.builder.QueryBuilder;
59 import org.onap.aai.schema.enums.ObjectMetadata;
60 import org.onap.aai.schema.enums.PropertyMetadata;
61 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
62 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
63 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
64 import org.onap.aai.serialization.engines.query.QueryEngine;
65 import org.onap.aai.serialization.tinkerpop.TreeBackedVertex;
66 import org.onap.aai.setup.SchemaVersion;
67 import org.onap.aai.setup.SchemaVersions;
68 import org.onap.aai.util.AAIConfig;
69 import org.onap.aai.util.AAIConstants;
70 import org.onap.aai.workarounds.NamingExceptions;
71 import org.springframework.context.ApplicationContext;
72
73 import javax.ws.rs.core.UriBuilder;
74 import java.io.UnsupportedEncodingException;
75 import java.lang.reflect.Array;
76 import java.lang.reflect.InvocationTargetException;
77 import java.net.MalformedURLException;
78 import java.net.URI;
79 import java.net.URISyntaxException;
80 import java.util.*;
81 import java.util.concurrent.ExecutionException;
82 import java.util.concurrent.ExecutorService;
83 import java.util.concurrent.Future;
84 import java.util.function.Function;
85 import java.util.regex.Matcher;
86 import java.util.regex.Pattern;
87 import java.util.stream.Collectors;
88
89 public class DBSerializer {
90
91     private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
92
93     private static final String MISSING_REQUIRED_NODE_PROPERTY = "Vertex missing required aai-node-type property";
94
95     private final TransactionalGraphEngine engine;
96     private final String sourceOfTruth;
97     private final ModelType introspectionType;
98     private final SchemaVersion version;
99     private final Loader latestLoader;
100     private EdgeSerializer edgeSer;
101     private EdgeIngestor edgeRules;
102     private final Loader loader;
103     private final String baseURL;
104     private double dbTimeMsecs = 0;
105     private long currentTimeMillis;
106
107     private SchemaVersions schemaVersions;
108     private Set<String> namedPropNodes;
109     /**
110      * Instantiates a new DB serializer.
111      *
112      * @param version the version
113      * @param engine the engine
114      * @param introspectionType the introspection type
115      * @param sourceOfTruth the source of truth
116      * @throws AAIException
117      */
118     public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
119         this.engine = engine;
120         this.sourceOfTruth = sourceOfTruth;
121         this.introspectionType = introspectionType;
122         this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
123         SchemaVersion LATEST = schemaVersions.getDefaultVersion();
124         this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST);
125         this.version = version;
126         this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
127         this.namedPropNodes = this.latestLoader.getNamedPropNodes();
128         this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
129         this.currentTimeMillis = System.currentTimeMillis();
130         initBeans();
131     }
132
133     private void initBeans() {
134         //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
135         ApplicationContext ctx = SpringContextAware.getApplicationContext();
136         EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
137         setEdgeIngestor(ei);
138         EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
139         setEdgeSerializer(es);
140     }
141
142     private void backupESInit() {
143         setEdgeSerializer(new EdgeSerializer(this.edgeRules));
144     }
145
146     public void setEdgeSerializer(EdgeSerializer edgeSer) {
147         this.edgeSer = edgeSer;
148     }
149
150     public EdgeSerializer getEdgeSeriailizer() {
151         return this.edgeSer;
152     }
153
154     public void setEdgeIngestor(EdgeIngestor ei) {
155         this.edgeRules = ei;
156     }
157
158     public EdgeIngestor getEdgeIngestor() {
159         return this.edgeRules;
160     }
161
162     /**
163      * Touch standard vertex properties.
164      *
165      * @param v the v
166      * @param isNewVertex the is new vertex
167      */
168     public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
169         String timeNowInSec = Long.toString(currentTimeMillis);
170
171         if (isNewVertex) {
172             v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
173             v.property(AAIProperties.CREATED_TS, timeNowInSec);
174             v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString());
175         }
176         v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
177         v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
178         v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
179
180     }
181
182     private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
183
184         v.property(AAIProperties.NODE_TYPE, nodeType);
185         touchStandardVertexProperties(v, isNewVertex);
186
187     }
188
189
190     /**
191      * Creates the new vertex.
192      *
193      * @param wrappedObject the wrapped object
194      * @return the vertex
195      * @throws UnsupportedEncodingException the unsupported encoding exception
196      * @throws AAIException                 the AAI exception
197      */
198     public Vertex createNewVertex(Introspector wrappedObject) {
199         Vertex v;
200         try {
201             StopWatch.conditionalStart();
202             v = this.engine.tx().addVertex();
203             touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
204         } finally {
205             dbTimeMsecs += StopWatch.stopIfStarted();
206         }
207         return v;
208     }
209
210     /**
211      * Trim class name.
212      *
213      * @param className the class name
214      * @return the string
215      */
216     /*
217      * Removes the classpath from a class name
218      */
219     public String trimClassName(String className) {
220         String returnValue = "";
221
222         if (className.lastIndexOf('.') == -1) {
223             return className;
224         }
225         returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
226
227         return returnValue;
228     }
229
230     /**
231      * Serialize to db.
232      *
233      * @param obj        the obj
234      * @param v          the v
235      * @param uriQuery   the uri query
236      * @param identifier the identifier
237      * @throws SecurityException            the security exception
238      * @throws IllegalAccessException       the illegal access exception
239      * @throws IllegalArgumentException     the illegal argument exception
240      * @throws InvocationTargetException    the invocation target exception
241      * @throws InstantiationException       the instantiation exception
242      * @throws InterruptedException         the interrupted exception
243      * @throws NoSuchMethodException        the no such method exception
244      * @throws AAIException                 the AAI exception
245      * @throws UnsupportedEncodingException the unsupported encoding exception
246      * @throws AAIUnknownObjectException
247      */
248     public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
249         StopWatch.conditionalStart();
250         try {
251             if (uriQuery.isDependent()) {
252                 //try to find the parent
253                 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
254                 if (!vertices.isEmpty()) {
255                     Vertex parent = vertices.get(0);
256                     this.reflectDependentVertex(parent, v, obj, requestContext);
257                 } else {
258                     dbTimeMsecs += StopWatch.stopIfStarted();
259                     throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
260                 }
261             } else {
262                 serializeSingleVertex(v, obj, requestContext);
263             }
264
265         } catch (SchemaViolationException e) {
266             dbTimeMsecs += StopWatch.stopIfStarted();
267             throw new AAIException("AAI_6117", e);
268         }
269         dbTimeMsecs += StopWatch.stopIfStarted();
270     }
271
272     public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
273         StopWatch.conditionalStart();
274         try {
275             boolean isTopLevel = obj.isTopLevel();
276             if (isTopLevel) {
277                 addUriIfNeeded(v, obj.getURI());
278             }
279
280             processObject(obj, v, requestContext);
281             if (!isTopLevel) {
282                 URI uri = this.getURIForVertex(v);
283                 URIParser parser = new URIParser(this.loader, uri);
284                 if (parser.validate()) {
285                     addUriIfNeeded(v, uri.toString());
286                 }
287             }
288         } catch (SchemaViolationException e) {
289             throw new AAIException("AAI_6117", e);
290         } finally {
291             dbTimeMsecs += StopWatch.stopIfStarted();
292         }
293     }
294
295     private void addUriIfNeeded(Vertex v, String uri) {
296         VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
297         if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) {
298             v.property(AAIProperties.AAI_URI, uri);
299         }
300     }
301
302     /**
303      * Process object.
304      *
305      * @param <T> the generic type
306      * @param obj the obj
307      * @param v   the v
308      * @return the list
309      * @throws IllegalAccessException       the illegal access exception
310      * @throws IllegalArgumentException     the illegal argument exception
311      * @throws InvocationTargetException    the invocation target exception
312      * @throws InstantiationException       the instantiation exception
313      * @throws NoSuchMethodException        the no such method exception
314      * @throws SecurityException            the security exception
315      * @throws AAIException                 the AAI exception
316      * @throws UnsupportedEncodingException the unsupported encoding exception
317      * @throws AAIUnknownObjectException
318      */
319     /*
320      * Helper method for reflectToDb
321      * Handles all the property setting
322      */
323     private <T> List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
324         Set<String> properties = new LinkedHashSet<>(obj.getProperties());
325         properties.remove(AAIProperties.RESOURCE_VERSION);
326         List<Vertex> dependentVertexes = new ArrayList<>();
327         List<Vertex> processedVertexes = new ArrayList<>();
328         boolean isComplexType = false;
329         boolean isListType = false;
330         if (!obj.isContainer()) {
331             this.touchStandardVertexProperties(obj.getDbName(), v, false);
332         }
333         this.executePreSideEffects(obj, v);
334         for (String property : properties) {
335             Object value = null;
336             final String propertyType;
337             propertyType = obj.getType(property);
338             isComplexType = obj.isComplexType(property);
339             isListType = obj.isListType(property);
340             value = obj.getValue(property);
341
342             if (!(isComplexType || isListType)) {
343                 boolean canModify = this.canModify(obj, property, requestContext);
344
345                 if (canModify) {
346                     final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
347                     String dbProperty = property;
348                     if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
349                         dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
350                     }
351                     if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
352                         //data linked properties are ephemeral
353                         //they are populated dynamically on GETs
354                         continue;
355                     }
356                     if (value != null) {
357                         if (!value.equals(v.property(dbProperty).orElse(null))) {
358                             if (propertyType.toLowerCase().contains(".long")) {
359                                 v.property(dbProperty, new Integer(((Long) value).toString()));
360                             } else {
361                                 v.property(dbProperty, value);
362                             }
363                         }
364                     } else {
365                         v.property(dbProperty).remove();
366                     }
367                 }
368             } else if (isListType) {
369                 List<Object> list = (List<Object>) value;
370                 if (obj.isComplexGenericType(property)) {
371                     if (list != null) {
372                         for (Object o : list) {
373                             Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
374                             child.setURIChain(obj.getURI());
375                             processedVertexes.add(reflectDependentVertex(v, child, requestContext));
376                         }
377                     }
378                 } else {
379                     //simple list case
380                     engine.setListProperty(v, property, list);
381                 }
382             } else {
383                 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
384                 if (value != null) { //effectively ignore complex properties not included in the object we're processing
385                     if (value.getClass().isArray()) {
386
387                         int length = Array.getLength(value);
388                         for (int i = 0; i < length; i++) {
389                             Object arrayElement = Array.get(value, i);
390                             Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
391                             child.setURIChain(obj.getURI());
392                             processedVertexes.add(reflectDependentVertex(v, child, requestContext));
393
394                         }
395                     } else if (!property.equals("relationship-list")) {
396                         // container case
397                         Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
398                         if (introspector.isContainer()) {
399                             dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
400                             introspector.setURIChain(obj.getURI());
401
402                             processedVertexes.addAll(processObject(introspector, v, requestContext));
403
404                         } else {
405                             dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
406                             processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
407
408                         }
409                     } else if (property.equals("relationship-list")) {
410                         handleRelationships(obj, v);
411                     }
412                 }
413             }
414         }
415         this.writeThroughDefaults(v, obj);
416         /* handle those vertexes not touched */
417         for (Vertex toBeRemoved : processedVertexes) {
418             dependentVertexes.remove(toBeRemoved);
419         }
420         this.deleteItemsWithTraversal(dependentVertexes);
421
422         this.executePostSideEffects(obj, v);
423         return processedVertexes;
424     }
425
426     /**
427      * Handle relationships.
428      *
429      * @param obj    the obj
430      * @param vertex the vertex
431      * @throws SecurityException            the security exception
432      * @throws IllegalAccessException       the illegal access exception
433      * @throws IllegalArgumentException     the illegal argument exception
434      * @throws InvocationTargetException    the invocation target exception
435      * @throws UnsupportedEncodingException the unsupported encoding exception
436      * @throws AAIException                 the AAI exception
437      */
438     /*
439      * Handles the explicit relationships defined for an obj
440      */
441     private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
442
443
444         Introspector wrappedRl = obj.getWrappedValue("relationship-list");
445         processRelationshipList(wrappedRl, vertex);
446
447
448     }
449
450
451     /**
452      * Process relationship list.
453      *
454      * @param wrapped the wrapped
455      * @param v       the v
456      * @throws UnsupportedEncodingException the unsupported encoding exception
457      * @throws AAIException                 the AAI exception
458      */
459     private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
460
461         List<Object> relationships = (List<Object>) wrapped.getValue("relationship");
462
463         List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
464         List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
465
466         for (Object relationship : relationships) {
467             Edge e = null;
468             Vertex cousinVertex = null;
469             String label = null;
470             Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
471             QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
472
473             if (wrappedRel.hasProperty("relationship-label")) {
474                 label = wrappedRel.getValue("relationship-label");
475             }
476
477             List<Vertex> results = parser.getQueryBuilder().toList();
478             if (results.isEmpty()) {
479                 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
480                 ex.getTemplateVars().add(parser.getResultType());
481                 ex.getTemplateVars().add(parser.getUri().toString());
482                 throw ex;
483             } else {
484                 //still an issue if there's more than one
485                 cousinVertex = results.get(0);
486             }
487
488             if (cousinVertex != null) {
489                 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
490                 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
491                 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
492
493
494                 if (!edgeRules.hasRule(baseQ.build())) {
495                     throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
496                         + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label) : "") + ".");
497                 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
498                     throw new AAIException("AAI_6145");
499                 }
500
501                 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
502
503                 if (e == null) {
504                     addEdges.add(new Triplet<>(v, cousinVertex, label));
505                 } else {
506                     existingEdges.remove(e);
507                 }
508             }
509         }
510
511         for (Edge edge : existingEdges) {
512             edge.remove();
513         }
514         for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
515             try {
516                 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
517             } catch (NoEdgeRuleFoundException e) {
518                 throw new AAIException("AAI_6129", e);
519             }
520         }
521
522     }
523
524     /**
525      * Write through defaults.
526      *
527      * @param v   the v
528      * @param obj the obj
529      * @throws AAIUnknownObjectException
530      */
531     private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
532         Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
533         if (latest != null) {
534             Set<String> required = latest.getRequiredProperties();
535
536             for (String field : required) {
537                 String defaultValue = null;
538                 Object vertexProp = null;
539                 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
540                 if (defaultValue != null) {
541                     vertexProp = v.<Object>property(field).orElse(null);
542                     if (vertexProp == null) {
543                         v.property(field, defaultValue);
544                     }
545                 }
546             }
547         }
548
549     }
550
551
552     /**
553      * Reflect dependent vertex.
554      *
555      * @param v            the v
556      * @param dependentObj the dependent obj
557      * @return the vertex
558      * @throws IllegalAccessException       the illegal access exception
559      * @throws IllegalArgumentException     the illegal argument exception
560      * @throws InvocationTargetException    the invocation target exception
561      * @throws InstantiationException       the instantiation exception
562      * @throws NoSuchMethodException        the no such method exception
563      * @throws SecurityException            the security exception
564      * @throws AAIException                 the AAI exception
565      * @throws UnsupportedEncodingException the unsupported encoding exception
566      * @throws AAIUnknownObjectException
567      */
568     private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
569
570         //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
571         //List<Vertex> items = p.getQuery().toList();
572         QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
573         query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
574         query.createKeyQuery(dependentObj);
575
576         List<Vertex> items = query.toList();
577
578         Vertex dependentVertex = null;
579         if (items.size() == 1) {
580             dependentVertex = items.get(0);
581             this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
582         } else {
583             this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
584             dependentVertex = createNewVertex(dependentObj);
585         }
586
587         return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
588
589     }
590
591     /**
592      * Reflect dependent vertex.
593      *
594      * @param parent the parent
595      * @param child  the child
596      * @param obj    the obj
597      * @return the vertex
598      * @throws IllegalAccessException       the illegal access exception
599      * @throws IllegalArgumentException     the illegal argument exception
600      * @throws InvocationTargetException    the invocation target exception
601      * @throws InstantiationException       the instantiation exception
602      * @throws NoSuchMethodException        the no such method exception
603      * @throws SecurityException            the security exception
604      * @throws AAIException                 the AAI exception
605      * @throws UnsupportedEncodingException the unsupported encoding exception
606      * @throws AAIUnknownObjectException
607      */
608     private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
609
610         String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
611         if (parentUri != null) {
612             String uri;
613             uri = obj.getURI();
614             addUriIfNeeded(child, parentUri + uri);
615         }
616         processObject(obj, child, requestContext);
617
618         Edge e;
619         e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
620
621         if (e == null) {
622             String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
623             if (canBeLinked != null && canBeLinked.equals("true")) {
624                 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext));
625                 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
626                 if (isFirst) {
627                     child.property(AAIProperties.LINKED, true);
628                 }
629             }
630             edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
631         }
632         return child;
633
634     }
635
636     private SchemaVersion getVerForContext(String requestContext) {
637         Pattern pattern = Pattern.compile("v[0-9]+");
638         Matcher m = pattern.matcher(requestContext);
639         if (!m.find()) {
640             return this.version;
641         } else {
642             return new SchemaVersion(requestContext);
643         }
644     }
645
646     /**
647      * Db to object.
648      *
649      * @param vertices the vertices
650      * @param obj      the obj
651      * @param depth    the depth
652      * @param cleanUp  the clean up
653      * @return the introspector
654      * @throws AAIException                 the AAI exception
655      * @throws IllegalAccessException       the illegal access exception
656      * @throws IllegalArgumentException     the illegal argument exception
657      * @throws InvocationTargetException    the invocation target exception
658      * @throws SecurityException            the security exception
659      * @throws InstantiationException       the instantiation exception
660      * @throws NoSuchMethodException        the no such method exception
661      * @throws UnsupportedEncodingException the unsupported encoding exception
662      * @throws MalformedURLException        the malformed URL exception
663      * @throws AAIUnknownObjectException
664      * @throws URISyntaxException
665      */
666     public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
667         final int internalDepth;
668         if (depth == Integer.MAX_VALUE) {
669             internalDepth = depth--;
670         } else {
671             internalDepth = depth;
672         }
673         StopWatch.conditionalStart();
674         if (vertices.size() > 1 && !obj.isContainer()) {
675             dbTimeMsecs += StopWatch.stopIfStarted();
676             throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
677         } else if (obj.isContainer()) {
678             final List getList;
679             String listProperty = null;
680             for (String property : obj.getProperties()) {
681                 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
682                     listProperty = property;
683                     break;
684                 }
685             }
686             final String propertyName = listProperty;
687             getList = (List) obj.getValue(listProperty);
688
689             /* This is an experimental multithreading experiment
690              * on get alls.
691              */
692             ExecutorService pool = GetAllPool.getInstance().getPool();
693
694             List<Future<Object>> futures = new ArrayList<>();
695
696             QueryEngine tgEngine = this.engine.getQueryEngine();
697             for (Vertex v : vertices) {
698
699                 AaiCallable<Object> task = new AaiCallable<Object>() {
700                     @Override
701                     public Object process() throws UnsupportedEncodingException, AAIException {
702                         Set<Vertex> seen = new HashSet<>();
703                         Introspector childObject;
704                         try {
705                             childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
706                         } catch (AAIUnknownObjectException e) {
707                             throw e;
708                         }
709                         try {
710                             dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp);
711                         } catch (UnsupportedEncodingException e) {
712                             throw e;
713                         } catch (AAIException e) {
714                             throw e;
715                         }
716                         return childObject.getUnderlyingObject();
717                         //getList.add(childObject.getUnderlyingObject());
718                     }
719                 };
720                 futures.add(pool.submit(task));
721             }
722
723             for (Future<Object> future : futures) {
724                 try {
725                     getList.add(future.get());
726                 } catch (ExecutionException e) {
727                     dbTimeMsecs += StopWatch.stopIfStarted();
728                     throw new AAIException("AAI_4000", e);
729                 } catch (InterruptedException e) {
730                     dbTimeMsecs += StopWatch.stopIfStarted();
731                     throw new AAIException("AAI_4000", e);
732                 }
733             }
734         } else if (vertices.size() == 1) {
735             Set<Vertex> seen = new HashSet<>();
736             dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp);
737         } else {
738             //obj = null;
739         }
740
741         dbTimeMsecs += StopWatch.stopIfStarted();
742         return obj;
743     }
744
745     /**
746      * Db to object.
747      *
748      * @param obj     the obj
749      * @param v       the v
750      * @param seen    the seen
751      * @param depth   the depth
752      * @param cleanUp the clean up
753      * @return the introspector
754      * @throws IllegalAccessException       the illegal access exception
755      * @throws IllegalArgumentException     the illegal argument exception
756      * @throws InvocationTargetException    the invocation target exception
757      * @throws SecurityException            the security exception
758      * @throws InstantiationException       the instantiation exception
759      * @throws NoSuchMethodException        the no such method exception
760      * @throws UnsupportedEncodingException the unsupported encoding exception
761      * @throws AAIException                 the AAI exception
762      * @throws MalformedURLException        the malformed URL exception
763      * @throws AAIUnknownObjectException
764      * @throws URISyntaxException
765      */
766     private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
767
768         if (depth < 0) {
769             return null;
770         }
771         depth--;
772         seen.add(v);
773
774         boolean modified = false;
775         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
776             List<Object> getList = null;
777             Vertex[] vertices = null;
778
779             if (!(obj.isComplexType(property) || obj.isListType(property))) {
780                 this.copySimpleProperty(property, obj, v);
781                 modified = true;
782             } else {
783                 if (obj.isComplexType(property)) {
784                     /* container case */
785
786                     if (!property.equals("relationship-list") && depth >= 0) {
787                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
788                         Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
789                         if (result != null) {
790                             obj.setValue(property, argumentObject.getUnderlyingObject());
791                             modified = true;
792                         }
793                     } else if (property.equals("relationship-list") && !nodeOnly) {
794                         /* relationships need to be handled correctly */
795                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
796                         relationshipList = createRelationshipList(v, relationshipList, cleanUp);
797                         if (relationshipList != null) {
798                             modified = true;
799                             obj.setValue(property, relationshipList.getUnderlyingObject());
800                             modified = true;
801                         }
802
803                     }
804                 } else if (obj.isListType(property)) {
805
806                     if (property.equals("any")) {
807                         continue;
808                     }
809                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
810                     if (obj.isComplexGenericType(property) && depth >= 0) {
811                         final String childDbName = convertFromCamelCase(genericType);
812                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
813                         EdgeRule rule;
814
815                         try {
816                             rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
817                         } catch (EdgeRuleNotFoundException e) {
818                             throw new NoEdgeRuleFoundException(e);
819                         } catch (AmbiguousRuleChoiceException e) {
820                             throw new MultipleEdgeRuleFoundException(e);
821                         }
822                         if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
823                             //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
824                             Direction ruleDirection = rule.getDirection();
825                             Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
826                             List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
827                             itr = verticesList.stream().filter(item -> {
828                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
829                             }).iterator();
830                             if (itr.hasNext()) {
831                                 getList = (List<Object>) obj.getValue(property);
832                             }
833                             int processed = 0;
834                             int removed = 0;
835                             while (itr.hasNext()) {
836                                 Vertex childVertex = itr.next();
837                                 if (!seen.contains(childVertex)) {
838                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
839
840                                     Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
841                                     if (result != null) {
842                                         getList.add(argumentObject.getUnderlyingObject());
843                                     }
844
845                                     processed++;
846                                 } else {
847                                     removed++;
848                                     LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
849                                 }
850                             }
851                             if (processed == 0) {
852                                 //vertices were all seen, reset the list
853                                 getList = null;
854                             }
855                             if (processed > 0) {
856                                 modified = true;
857                             }
858                         }
859                     } else if (obj.isSimpleGenericType(property)) {
860                         List<Object> temp = this.engine.getListProperty(v, property);
861                         if (temp != null) {
862                             getList = (List<Object>) obj.getValue(property);
863                             getList.addAll(temp);
864                             modified = true;
865                         }
866
867                     }
868
869                 }
870
871             }
872         }
873
874         //no changes were made to this obj, discard the instance
875         if (!modified) {
876             return null;
877         }
878         this.enrichData(obj, v);
879         return obj;
880
881     }
882
883
884     public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
885         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
886         if (nodeType == null) {
887             throw new AAIException("AAI_6143");
888         }
889
890         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
891         Set<Vertex> seen = new HashSet<>();
892         int depth = 0;
893         String cleanUp = "false";
894         boolean nodeOnly = true;
895         StopWatch.conditionalStart();
896         this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
897         dbTimeMsecs += StopWatch.stopIfStarted();
898         return obj;
899
900     }
901
902     public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
903         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
904         if (nodeType == null) {
905             throw new AAIException("AAI_6143");
906         }
907         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
908         Set<Vertex> seen = new HashSet<>();
909         int depth = AAIProperties.MAXIMUM_DEPTH;
910         String cleanUp = "false";
911         boolean nodeOnly = false;
912         StopWatch.conditionalStart();
913         Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
914         TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
915         this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
916         dbTimeMsecs += StopWatch.stopIfStarted();
917         return obj;
918     }
919
920     /**
921      * Copy simple property.
922      *
923      * @param property the property
924      * @param obj      the obj
925      * @param v        the v
926      * @throws InstantiationException    the instantiation exception
927      * @throws IllegalAccessException    the illegal access exception
928      * @throws IllegalArgumentException  the illegal argument exception
929      * @throws InvocationTargetException the invocation target exception
930      * @throws NoSuchMethodException     the no such method exception
931      * @throws SecurityException         the security exception
932      */
933     private void copySimpleProperty(String property, Introspector obj, Vertex v) {
934         final Object temp = getProperty(obj, property, v);
935         if (temp != null) {
936             obj.setValue(property, temp);
937         }
938     }
939
940
941     /**
942      * Load the introspector from the hashmap for the given property key
943      *
944      * @param property - vertex property
945      * @param obj      - introspector object representing the vertex
946      * @param hashMap  - Containing a list of pre-fetched properties for a given vertex
947      */
948     private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap){
949
950         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
951         String dbPropertyName = property;
952
953         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
954             dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
955         }
956
957         final Object temp = hashMap.getOrDefault(dbPropertyName, null);
958
959         if (temp != null) {
960             obj.setValue(property, temp);
961         }
962     }
963
964     /**
965      * Simple db to object.
966      *
967      * @param obj the obj
968      * @param v   the v
969      * @throws InstantiationException    the instantiation exception
970      * @throws IllegalAccessException    the illegal access exception
971      * @throws IllegalArgumentException  the illegal argument exception
972      * @throws InvocationTargetException the invocation target exception
973      * @throws NoSuchMethodException     the no such method exception
974      * @throws SecurityException         the security exception
975      */
976     private void simpleDbToObject(Introspector obj, Vertex v) {
977         for(String key : obj.getProperties()){
978             this.copySimpleProperty(key, obj, v);
979         }
980     }
981
982
983     public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v){
984
985         long startTime = System.currentTimeMillis();
986
987         Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
988         String[] simplePropsArray    = new String[simpleProperties.size()];
989         simplePropsArray             = simpleProperties.toArray(simplePropsArray);
990
991         Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
992
993         v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value()));
994
995         return simplePropsHashMap;
996     }
997
998     public Introspector dbToRelationshipObject(Vertex v) throws UnsupportedEncodingException, AAIException {
999         Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1000         relationshipList =  createRelationshipList(v, relationshipList, "false");
1001         return relationshipList;
1002     }
1003     /**
1004      * Creates the relationship list.
1005      *
1006      * @param v       the v
1007      * @param obj     the obj
1008      * @param cleanUp the clean up
1009      * @return the object
1010      * @throws InstantiationException       the instantiation exception
1011      * @throws IllegalAccessException       the illegal access exception
1012      * @throws IllegalArgumentException     the illegal argument exception
1013      * @throws InvocationTargetException    the invocation target exception
1014      * @throws NoSuchMethodException        the no such method exception
1015      * @throws SecurityException            the security exception
1016      * @throws UnsupportedEncodingException the unsupported encoding exception
1017      * @throws AAIException                 the AAI exception
1018      * @throws MalformedURLException        the malformed URL exception
1019      * @throws URISyntaxException
1020      */
1021     private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
1022
1023         String[] cousinRules = new String[0];
1024
1025         try {
1026             cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName());
1027         } catch (ExecutionException e) {
1028             LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", obj.getDbName(), e);
1029         }
1030
1031         List<Vertex> cousins = null;
1032         if(cousinRules != null && cousinRules.length != 0){
1033             cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules);
1034         } else {
1035             cousins = this.engine.getQueryEngine().findCousinVertices(v);
1036         }
1037
1038         List<Object> relationshipObjList = obj.getValue("relationship");
1039         VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1040
1041         if(!nodeTypeProperty.isPresent()){
1042             LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY);
1043             LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1044             LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString());
1045             return null;
1046         }
1047
1048         String aNodeType = nodeTypeProperty.value().toString();
1049
1050         TypeAlphabetizer alphabetizer = new TypeAlphabetizer();
1051
1052         EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1053         Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys();
1054
1055         // For the given vertex, find all the cousins
1056         // For each cousin retrieve the node type and then
1057         // check if the version is greater than the edge label version
1058         // meaning is the current version equal to greater than the version
1059         // where we introduced the edge labels into the relationship payload
1060         // If it is, then we check if the edge key there are multiple labels
1061         // If there are multiple labels, then we need to go to the database
1062         // to retrieve the labels between itself and cousin vertex
1063         // If there is only single label between the edge a and b, then
1064         // we can retrieve what that is without going to the database
1065         // from using the edge rules json and get the edge rule out of it
1066         EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType);
1067         for (Vertex cousin : cousins) {
1068             VertexProperty vertexProperty = cousin.property(AAIProperties.NODE_TYPE);
1069             String bNodeType = null;
1070             if(vertexProperty.isPresent()){
1071                 bNodeType = cousin.property(AAIProperties.NODE_TYPE).value().toString();
1072             } else {
1073                 // If the vertex is missing the aai-node-type
1074                 // Then its either a bad vertex or its in the process
1075                 // of getting deleted so we should ignore these vertexes
1076                 LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY);
1077                 if(LOGGER.isDebugEnabled()){
1078                     LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString());
1079                 } else {
1080                     LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log");
1081                 }
1082                 LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString());
1083                 continue;
1084             }
1085             if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) {
1086                 String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType);
1087                 if(keysWithMultipleLabels.contains(edgeKey)){
1088                     List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin);
1089                     for(String edgeLabel: edgeLabels){
1090                         Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1091                         Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel);
1092                         if (result != null) {
1093                             relationshipObjList.add(result);
1094                         }
1095                     }
1096                 } else {
1097
1098                     EdgeRule edgeRule = null;
1099
1100                     // Create a query based on the a nodetype and b nodetype
1101                     // which is also a cousin edge and ensure the version
1102                     // is used properly so for example in order to be backwards
1103                     // compatible if we had allowed a edge between a and b
1104                     // in a previous release and we decided to remove it from
1105                     // the edge rules in the future we can display the edge
1106                     // only for the older apis and the new apis if the edge rule
1107                     // is removed will not be seen in the newer version of the API
1108
1109                     EdgeRuleQuery ruleQuery = queryBuilder
1110                         .to(bNodeType)
1111                         .edgeType(EdgeType.COUSIN)
1112                         .version(obj.getVersion())
1113                         .build();
1114
1115                     try {
1116                         edgeRule = edgeIngestor.getRule(ruleQuery);
1117                     } catch (EdgeRuleNotFoundException e) {
1118                         LOGGER.warn("Caught an edge rule not found exception for query {}, {}," +
1119                             " it could be the edge rule is no longer valid for the existing edge in db",
1120                             ruleQuery, LogFormatTools.getStackTop(e));
1121                         continue;
1122                     } catch (AmbiguousRuleChoiceException e) {
1123                         LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}",
1124                             ruleQuery, LogFormatTools.getStackTop(e));
1125                         continue;
1126                     }
1127
1128                     Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1129                     Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp,edgeRule.getLabel());
1130                     if (result != null) {
1131                         relationshipObjList.add(result);
1132                     }
1133                 }
1134             } else {
1135                 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1136                 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1137                 if (result != null) {
1138                     relationshipObjList.add(result);
1139                 }
1140             }
1141
1142         }
1143
1144         if (relationshipObjList.isEmpty()) {
1145             return null;
1146         } else {
1147             return obj;
1148         }
1149     }
1150
1151     /**
1152      * Process edge relationship.
1153      *
1154      * @param relationshipObj the relationship obj
1155      * @param edge            the edge
1156      * @param cleanUp         the clean up
1157      * @return the object
1158      * @throws InstantiationException       the instantiation exception
1159      * @throws IllegalAccessException       the illegal access exception
1160      * @throws IllegalArgumentException     the illegal argument exception
1161      * @throws InvocationTargetException    the invocation target exception
1162      * @throws NoSuchMethodException        the no such method exception
1163      * @throws SecurityException            the security exception
1164      * @throws UnsupportedEncodingException the unsupported encoding exception
1165      * @throws AAIException                 the AAI exception
1166      * @throws MalformedURLException        the malformed URL exception
1167      * @throws AAIUnknownObjectException
1168      * @throws URISyntaxException
1169      */
1170     private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException {
1171
1172         VertexProperty aaiUriProperty = cousin.property("aai-uri");
1173
1174         if(!aaiUriProperty.isPresent()){
1175             return null;
1176         }
1177
1178         URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1179
1180         URIToRelationshipObject uriParser = null;
1181         Introspector result = null;
1182         try {
1183             uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1184             result = uriParser.getResult();
1185         } catch (AAIException | URISyntaxException e) {
1186             LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + ": "
1187                 + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1188             return null;
1189         }
1190
1191         VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1192
1193         if(cousinVertexNodeType.isPresent()){
1194             String cousinType = cousinVertexNodeType.value().toString();
1195             if(namedPropNodes.contains(cousinType)){
1196                 this.addRelatedToProperty(result, cousin, cousinType);
1197             }
1198         }
1199
1200         if (edgeLabel != null && result.hasProperty("relationship-label")) {
1201             result.setValue("relationship-label", edgeLabel);
1202         }
1203
1204         return result.getUnderlyingObject();
1205     }
1206
1207     /**
1208      * Gets the URI for vertex.
1209      *
1210      * @param v the v
1211      * @return the URI for vertex
1212      * @throws InstantiationException       the instantiation exception
1213      * @throws IllegalAccessException       the illegal access exception
1214      * @throws IllegalArgumentException     the illegal argument exception
1215      * @throws InvocationTargetException    the invocation target exception
1216      * @throws NoSuchMethodException        the no such method exception
1217      * @throws SecurityException            the security exception
1218      * @throws UnsupportedEncodingException the unsupported encoding exception
1219      * @throws AAIUnknownObjectException
1220      */
1221     public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1222
1223         return getURIForVertex(v, false);
1224     }
1225
1226     public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
1227         URI uri = UriBuilder.fromPath("/unknown-uri").build();
1228
1229         String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1230
1231         if (aaiUri != null && !overwrite) {
1232             uri = UriBuilder.fromPath(aaiUri).build();
1233         }
1234
1235         return uri;
1236     }
1237
1238     /**
1239      * Gets the URI from list.
1240      *
1241      * @param list the list
1242      * @return the URI from list
1243      * @throws UnsupportedEncodingException the unsupported encoding exception
1244      */
1245     private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1246         String uri = "";
1247         StringBuilder sb = new StringBuilder();
1248         for (Introspector i : list) {
1249             sb.insert(0, i.getURI());
1250         }
1251
1252         uri = sb.toString();
1253         return UriBuilder.fromPath(uri).build();
1254     }
1255
1256     public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType) throws AAIUnknownObjectException {
1257         Introspector obj = this.latestLoader.introspectorFromName(cousinType);
1258         String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1259         List<Introspector> relatedToProperties = new ArrayList<>();
1260
1261         if (nameProps != null) {
1262             String[] props = nameProps.split(",");
1263             for (String prop : props) {
1264                 final Object temp = getProperty(obj, prop, cousinVertex);
1265                 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1266                 relatedTo.setValue("property-key", cousinType + "." + prop);
1267                 relatedTo.setValue("property-value", temp);
1268                 relatedToProperties.add(relatedTo);
1269             }
1270         }
1271
1272         if (!relatedToProperties.isEmpty()) {
1273             List relatedToList = (List) relationship.getValue("related-to-property");
1274             for (Introspector introspector : relatedToProperties) {
1275                 relatedToList.add(introspector.getUnderlyingObject());
1276             }
1277         }
1278
1279     }
1280
1281     private Object getProperty(Introspector obj, String prop, Vertex vertex){
1282
1283         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1284         String dbPropertyName = prop;
1285
1286         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1287             dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1288         }
1289
1290         return vertex.<Object>property(dbPropertyName).orElse(null);
1291     }
1292
1293     /**
1294      * Creates the edge.
1295      *
1296      * @param relationship the relationship
1297      * @param inputVertex  the input vertex
1298      * @return true, if successful
1299      * @throws UnsupportedEncodingException the unsupported encoding exception
1300      * @throws AAIException                 the AAI exception
1301      */
1302     public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1303
1304         Vertex relatedVertex = null;
1305         StopWatch.conditionalStart();
1306         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1307
1308         String label = null;
1309         if (relationship.hasProperty("relationship-label")) {
1310             label = relationship.getValue("relationship-label");
1311         }
1312
1313         List<Vertex> results = parser.getQueryBuilder().toList();
1314         if (results.isEmpty()) {
1315             dbTimeMsecs += StopWatch.stopIfStarted();
1316             AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1317             e.getTemplateVars().add(parser.getResultType());
1318             e.getTemplateVars().add(parser.getUri().toString());
1319             throw e;
1320         } else {
1321             //still an issue if there's more than one
1322             relatedVertex = results.get(0);
1323         }
1324
1325         if (relatedVertex != null) {
1326
1327             Edge e;
1328             try {
1329                 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1330                 if (e == null) {
1331                     edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1332                 } else {
1333                     //attempted to link two vertexes already linked
1334                 }
1335             } finally {
1336                 dbTimeMsecs += StopWatch.stopIfStarted();
1337             }
1338         }
1339
1340         dbTimeMsecs += StopWatch.stopIfStarted();
1341         return true;
1342     }
1343
1344     /**
1345      * Gets all the edges between of the type with the specified label.
1346      *
1347      * @param aVertex the out vertex
1348      * @param bVertex the in vertex
1349      * @return the edges between
1350      * @throws AAIException             the AAI exception
1351      * @throws NoEdgeRuleFoundException
1352      */
1353     private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1354
1355         Edge result = null;
1356
1357         if (bVertex != null) {
1358             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1359             if (EdgeType.TREE.equals(type)) {
1360                 GraphTraversal<Vertex,Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1361                 if(edgeRule.getDirection().equals(Direction.IN)){
1362                     findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1363                         .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1364                         .not(
1365                             __.has(EdgeField.PRIVATE.toString(), true)
1366                         );
1367                 } else {
1368                     findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1369                         .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1370                         .not(
1371                             __.has(EdgeField.PRIVATE.toString(), true)
1372                         );
1373                 }
1374                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1375             } else {
1376                 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1377                 findEdgesBetween = findEdgesBetween
1378                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1379                     .not(
1380                         __.has(EdgeField.PRIVATE.toString(), true)
1381                     );
1382                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1383             }
1384             List<Edge> list = findEdgesBetween.toList();
1385             if(!list.isEmpty()){
1386                 result = list.get(0);
1387             }
1388         }
1389
1390         return result;
1391     }
1392
1393     /**
1394      * Gets all the edges between of the type.
1395      *
1396      * @param aVertex the out vertex
1397      * @param bVertex the in vertex
1398      * @return the edges between
1399      * @throws AAIException             the AAI exception
1400      * @throws NoEdgeRuleFoundException
1401      */
1402     private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1403
1404         List<Edge> result = new ArrayList<>();
1405
1406         if (bVertex != null) {
1407             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1408             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1409             if (EdgeType.TREE.equals(type)) {
1410                 findEdgesBetween = findEdgesBetween
1411                     .not(
1412                         __.or(
1413                             __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1414                             __.has(EdgeField.PRIVATE.toString(), true)
1415                         )
1416                     );
1417             } else {
1418                 findEdgesBetween = findEdgesBetween
1419                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1420                     .not(
1421                         __.has(EdgeField.PRIVATE.toString(), true)
1422                     );
1423             }
1424             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1425             result = findEdgesBetween.toList();
1426         }
1427
1428         return result;
1429     }
1430
1431     /**
1432      * Gets all the edges string between of the type.
1433      *
1434      * @param aVertex the out vertex
1435      * @param bVertex the in vertex
1436      * @return the edges between
1437      * @throws AAIException the AAI exception
1438      * @throws NoEdgeRuleFoundException
1439      */
1440     private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1441
1442         List<String> result = new ArrayList<>();
1443
1444         if (bVertex != null) {
1445             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1446             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1447             if (EdgeType.TREE.equals(type)) {
1448                 findEdgesBetween = findEdgesBetween
1449                         .not(
1450                                 __.or(
1451                                         __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1452                                         __.has(EdgeField.PRIVATE.toString(), true)
1453                                 )
1454                         );
1455             } else {
1456                 findEdgesBetween = findEdgesBetween
1457                         .has(EdgeProperty.CONTAINS.toString(), "NONE")
1458                         .not(
1459                                 __.has(EdgeField.PRIVATE.toString(), true)
1460                         );
1461             }
1462             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1463             result = findEdgesBetween.label().toList();
1464         }
1465         return result;
1466     }
1467
1468     /**
1469      * Gets all the edges string between of the type.
1470      *
1471      * @param aVertex the out vertex
1472      * @param bVertex the in vertex
1473      * @return the edges between
1474      * @throws AAIException the AAI exception
1475      * @throws NoEdgeRuleFoundException
1476      */
1477     private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) {
1478
1479         Long result = null;
1480
1481         if (bVertex != null) {
1482             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1483             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1484             findEdgesBetween = findEdgesBetween
1485                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1486                     .not(
1487                             __.has(EdgeField.PRIVATE.toString(), true)
1488                     );
1489             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1490             result = findEdgesBetween.count().next();
1491         }
1492         return result;
1493     }
1494     /**
1495      * Gets all the edges between the vertexes with the label and type.
1496      *
1497      * @param aVertex the out vertex
1498      * @param bVertex the in vertex
1499      * @param label
1500      * @return the edges between
1501      * @throws AAIException the AAI exception
1502      */
1503     private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1504
1505         Edge edge = null;
1506
1507         if (bVertex != null) {
1508             String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1509             String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1510             EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1511             EdgeRule rule;
1512             try {
1513                 rule = edgeRules.getRule(q);
1514             } catch (EdgeRuleNotFoundException e) {
1515                 throw new NoEdgeRuleFoundException(e);
1516             } catch (AmbiguousRuleChoiceException e) {
1517                 throw new MultipleEdgeRuleFoundException(e);
1518             }
1519             edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1520         }
1521
1522         return edge;
1523     }
1524
1525     /**
1526      * Gets the edge between with the label and edge type.
1527      *
1528      * @param aVertex the out vertex
1529      * @param bVertex the in vertex
1530      * @param label
1531      * @return the edge between
1532      * @throws AAIException             the AAI exception
1533      * @throws NoEdgeRuleFoundException
1534      */
1535     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1536
1537         StopWatch.conditionalStart();
1538         if (bVertex != null) {
1539
1540             Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1541             if (edge != null) {
1542                 dbTimeMsecs += StopWatch.stopIfStarted();
1543                 return edge;
1544             }
1545
1546         }
1547         dbTimeMsecs += StopWatch.stopIfStarted();
1548         return null;
1549     }
1550
1551     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1552         return this.getEdgeBetween(type, aVertex, bVertex, null);
1553     }
1554
1555
1556     /**
1557      * Delete edge.
1558      *
1559      * @param relationship the relationship
1560      * @param inputVertex  the input vertex
1561      * @return true, if successful
1562      * @throws UnsupportedEncodingException the unsupported encoding exception
1563      * @throws AAIException                 the AAI exception
1564      */
1565     public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1566
1567         Vertex relatedVertex = null;
1568         StopWatch.conditionalStart();
1569         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1570
1571         List<Vertex> results = parser.getQueryBuilder().toList();
1572
1573         String label = null;
1574         if (relationship.hasProperty("relationship-label")) {
1575             label = relationship.getValue("relationship-label");
1576         }
1577
1578         if (results.isEmpty()) {
1579             dbTimeMsecs += StopWatch.stopIfStarted();
1580             return false;
1581         }
1582
1583         relatedVertex = results.get(0);
1584         Edge edge;
1585         try {
1586             edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1587         } catch (NoEdgeRuleFoundException e) {
1588             dbTimeMsecs += StopWatch.stopIfStarted();
1589             throw new AAIException("AAI_6129", e);
1590         }
1591         if (edge != null) {
1592             edge.remove();
1593             dbTimeMsecs += StopWatch.stopIfStarted();
1594             return true;
1595         } else {
1596             dbTimeMsecs += StopWatch.stopIfStarted();
1597             return false;
1598         }
1599
1600     }
1601
1602     /**
1603      * Delete items with traversal.
1604      *
1605      * @param vertexes the vertexes
1606      * @throws IllegalStateException the illegal state exception
1607      */
1608     public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1609
1610         for (Vertex v : vertexes) {
1611             LOGGER.debug("About to delete the vertex with id: " + v.id());
1612             deleteWithTraversal(v);
1613         }
1614
1615     }
1616
1617     /**
1618      * Delete with traversal.
1619      *
1620      * @param startVertex the start vertex
1621      */
1622     public void deleteWithTraversal(Vertex startVertex) {
1623         StopWatch.conditionalStart();
1624         List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1625
1626         for (Vertex v : results) {
1627             LOGGER.warn("Removing vertex " + v.id().toString());
1628
1629             v.remove();
1630         }
1631         dbTimeMsecs += StopWatch.stopIfStarted();
1632     }
1633
1634     /**
1635      * Delete.
1636      *
1637      * @param v               the v
1638      * @param resourceVersion the resource version
1639      * @throws IllegalArgumentException the illegal argument exception
1640      * @throws AAIException             the AAI exception
1641      * @throws InterruptedException     the interrupted exception
1642      */
1643     public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1644
1645         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1646         /*
1647          * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1648          * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted
1649          * So its better to make these in 2 steps
1650          */
1651         if (result && !deletableVertices.isEmpty()) {
1652             result = verifyPreventDeleteSemantics(deletableVertices);
1653         }
1654         if (result) {
1655
1656             try {
1657                 deleteWithTraversal(v);
1658             } catch (IllegalStateException e) {
1659                 throw new AAIException("AAI_6110", e);
1660             }
1661
1662         }
1663
1664     }
1665
1666
1667     /**
1668      * Delete.
1669      *
1670      * @param v               the v
1671      * @param resourceVersion the resource version
1672      * @throws IllegalArgumentException the illegal argument exception
1673      * @throws AAIException             the AAI exception
1674      * @throws InterruptedException     the interrupted exception
1675      */
1676     public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1677
1678         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1679
1680         if (result) {
1681
1682             try {
1683                 deleteWithTraversal(v);
1684             } catch (IllegalStateException e) {
1685                 throw new AAIException("AAI_6110", e);
1686             }
1687
1688         }
1689
1690     }
1691
1692     /**
1693      * Verify delete semantics.
1694      *
1695      * @param vertex          the vertex
1696      * @param resourceVersion the resource version
1697      * @return true, if successful
1698      * @throws AAIException the AAI exception
1699      */
1700     private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1701         boolean result = true;
1702         String nodeType = "";
1703         String errorDetail = " unknown delete semantic found";
1704         String aaiExceptionCode = "";
1705         nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1706         if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1707         }
1708         List<Vertex> vertices = new ArrayList<Vertex>();
1709         vertices.add(vertex);
1710         result = verifyPreventDeleteSemantics(vertices);
1711
1712         return result;
1713     }
1714
1715     /**
1716      * Verify Prevent delete semantics.
1717      *
1718      * @param vertices the list of vertices
1719      * @return true, if successful
1720      * @throws AAIException the AAI exception
1721      */
1722     private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
1723         boolean result = true;
1724         String nodeType = "";
1725         String errorDetail = " unknown delete semantic found";
1726         String aaiExceptionCode = "";
1727
1728         StopWatch.conditionalStart();
1729         /*
1730          * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition
1731          * If yes - that should prevent the deletion of the vertex
1732          * Dedup makes sure we dont capture the prevent-delete vertices twice
1733          * The prevent-delete vertices are stored so that the error message displays what prevents the delete
1734          */
1735
1736         List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices).
1737             union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE),
1738                 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE))
1739             .dedup().toList();
1740
1741         dbTimeMsecs += StopWatch.stopIfStarted();
1742         if (!preventDeleteVertices.isEmpty()) {
1743             aaiExceptionCode = "AAI_6110";
1744             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);
1745             result = false;
1746         }
1747         if (!result) {
1748             throw new AAIException(aaiExceptionCode, errorDetail);
1749         }
1750         return result;
1751     }
1752
1753     /**
1754      * Verify resource version.
1755      *
1756      * @param action                 the action
1757      * @param nodeType               the node type
1758      * @param currentResourceVersion the current resource version
1759      * @param resourceVersion        the resource version
1760      * @param uri                    the uri
1761      * @return true, if successful
1762      * @throws AAIException the AAI exception
1763      */
1764     public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1765         String enabled = "";
1766         String errorDetail = "";
1767         String aaiExceptionCode = "";
1768         boolean isDeleteResourceVersionOk = true;
1769         if (currentResourceVersion == null) {
1770             currentResourceVersion = "";
1771         }
1772
1773         if (resourceVersion == null) {
1774             resourceVersion = "";
1775         }
1776         try {
1777             enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1778
1779         } catch (AAIException e) {
1780             ErrorLogHelper.logException(e);
1781         }
1782         if (enabled.equals("true")) {
1783             if ("delete".equals(action)) {
1784                 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
1785             }
1786             if ((!isDeleteResourceVersionOk) || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
1787                 if ("create".equals(action) && !resourceVersion.equals("")) {
1788                     errorDetail = "resource-version passed for " + action + " of " + uri;
1789                     aaiExceptionCode = "AAI_6135";
1790                 } else if (resourceVersion.equals("")) {
1791                     errorDetail = "resource-version not passed for " + action + " of " + uri;
1792                     aaiExceptionCode = "AAI_6130";
1793                 } else {
1794                     errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1795                     aaiExceptionCode = "AAI_6131";
1796                 }
1797
1798                 throw new AAIException(aaiExceptionCode, errorDetail);
1799
1800             }
1801         }
1802         return true;
1803     }
1804
1805     /**
1806      * Verify resource version for delete.
1807      *
1808      * @param currentResourceVersion the current resource version
1809      * @param resourceVersion        the resource version
1810      * @return true, if successful or false if there is a mismatch
1811      */
1812     private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
1813
1814         boolean isDeleteResourceVersionOk = true;
1815         String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
1816             AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
1817
1818         if ((!currentResourceVersion.equals(resourceVersion)) && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
1819             isDeleteResourceVersionOk = false;
1820         }
1821         return isDeleteResourceVersionOk;
1822     }
1823
1824     /**
1825      * Convert from camel case.
1826      *
1827      * @param name the name
1828      * @return the string
1829      */
1830     private String convertFromCamelCase(String name) {
1831         String result = "";
1832         result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1833
1834         NamingExceptions exceptions = NamingExceptions.getInstance();
1835         result = exceptions.getDBName(result);
1836
1837         return result;
1838     }
1839
1840     private boolean canModify(Introspector obj, String propName, String requestContext) {
1841         final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1842         if (readOnly != null) {
1843             final String[] items = readOnly.split(",");
1844             for (String item : items) {
1845                 if (requestContext.equals(item)) {
1846                     return false;
1847                 }
1848             }
1849         }
1850         return true;
1851     }
1852
1853     private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1854
1855         SideEffectRunner runner = new SideEffectRunner
1856             .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build();
1857
1858         runner.execute(obj, self);
1859     }
1860
1861     private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1862
1863         SideEffectRunner runner = new SideEffectRunner
1864             .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1865
1866         runner.execute(obj, self);
1867     }
1868
1869     private void enrichData(Introspector obj, Vertex self) throws AAIException {
1870
1871         SideEffectRunner runner = new SideEffectRunner
1872             .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1873
1874         runner.execute(obj, self);
1875     }
1876
1877     public double getDBTimeMsecs() {
1878         return (dbTimeMsecs);
1879     }
1880
1881     /**
1882      * Db to object With Filters
1883      * This is for a one-time run with Tenant Isloation to only filter relationships
1884      * TODO: Chnage the original dbToObject to take filter parent/cousins
1885      *
1886      * @param obj               the obj
1887      * @param v                 the vertex from the graph
1888      * @param depth             the depth
1889      * @param nodeOnly          specify if to exclude relationships or not
1890      * @param filterCousinNodes
1891      * @return the introspector
1892      * @throws AAIException                 the AAI exception
1893      * @throws IllegalAccessException       the illegal access exception
1894      * @throws IllegalArgumentException     the illegal argument exception
1895      * @throws InvocationTargetException    the invocation target exception
1896      * @throws SecurityException            the security exception
1897      * @throws InstantiationException       the instantiation exception
1898      * @throws NoSuchMethodException        the no such method exception
1899      * @throws UnsupportedEncodingException the unsupported encoding exception
1900      * @throws MalformedURLException        the malformed URL exception
1901      * @throws AAIUnknownObjectException
1902      * @throws URISyntaxException
1903      */
1904     //TODO - See if you can merge the 2 dbToObjectWithFilters
1905     public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException {
1906         String cleanUp = "false";
1907         if (depth < 0) {
1908             return null;
1909         }
1910         depth--;
1911         seen.add(v);
1912         boolean modified = false;
1913         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1914             List<Object> getList = null;
1915             Vertex[] vertices = null;
1916
1917             if (!(obj.isComplexType(property) || obj.isListType(property))) {
1918                 this.copySimpleProperty(property, obj, v);
1919                 modified = true;
1920             } else {
1921                 if (obj.isComplexType(property)) {
1922                     /* container case */
1923
1924                     if (!property.equals("relationship-list") && depth >= 0) {
1925                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1926                         Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, filterCousinNodes, filterParentNodes);
1927                         if (result != null) {
1928                             obj.setValue(property, argumentObject.getUnderlyingObject());
1929                             modified = true;
1930                         }
1931                     } else if (property.equals("relationship-list") && !nodeOnly) {
1932                         /* relationships need to be handled correctly */
1933                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1934                         relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes);
1935                         if (relationshipList != null) {
1936                             modified = true;
1937                             obj.setValue(property, relationshipList.getUnderlyingObject());
1938                             modified = true;
1939                         }
1940
1941                     }
1942                 } else if (obj.isListType(property)) {
1943
1944                     if (property.equals("any")) {
1945                         continue;
1946                     }
1947                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
1948                     if (obj.isComplexGenericType(property) && depth >= 0) {
1949                         final String childDbName = convertFromCamelCase(genericType);
1950                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1951                         EdgeRule rule;
1952
1953                         boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains);
1954
1955                         EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
1956
1957                         try {
1958                             rule = edgeRules.getRule(q);
1959                         } catch (EdgeRuleNotFoundException e) {
1960                             throw new NoEdgeRuleFoundException(e);
1961                         } catch (AmbiguousRuleChoiceException e) {
1962                             throw new MultipleEdgeRuleFoundException(e);
1963                         }
1964                         if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) {
1965                             //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
1966                             Direction ruleDirection = rule.getDirection();
1967                             Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
1968                             List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
1969                             itr = verticesList.stream().filter(item -> {
1970                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
1971                             }).iterator();
1972                             if (itr.hasNext()) {
1973                                 getList = (List<Object>) obj.getValue(property);
1974                             }
1975                             int processed = 0;
1976                             int removed = 0;
1977                             while (itr.hasNext()) {
1978                                 Vertex childVertex = itr.next();
1979                                 if (!seen.contains(childVertex)) {
1980                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1981
1982                                     Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes);
1983                                     if (result != null) {
1984                                         getList.add(argumentObject.getUnderlyingObject());
1985                                     }
1986
1987                                     processed++;
1988                                 } else {
1989                                     removed++;
1990                                     LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
1991                                 }
1992                             }
1993                             if (processed == 0) {
1994                                 //vertices were all seen, reset the list
1995                                 getList = null;
1996                             }
1997                             if (processed > 0) {
1998                                 modified = true;
1999                             }
2000                         }
2001                     } else if (obj.isSimpleGenericType(property)) {
2002                         List<Object> temp = this.engine.getListProperty(v, property);
2003                         if (temp != null) {
2004                             getList = (List<Object>) obj.getValue(property);
2005                             getList.addAll(temp);
2006                             modified = true;
2007                         }
2008
2009                     }
2010
2011                 }
2012
2013             }
2014         }
2015
2016         //no changes were made to this obj, discard the instance
2017         if (!modified) {
2018             return null;
2019         }
2020         this.enrichData(obj, v);
2021         return obj;
2022
2023     }
2024
2025     /**
2026      * Creates the relationship list with the filtered node types.
2027      *
2028      * @param v       the v
2029      * @param obj     the obj
2030      * @param cleanUp the clean up
2031      * @return the object
2032      * @throws InstantiationException       the instantiation exception
2033      * @throws IllegalAccessException       the illegal access exception
2034      * @throws IllegalArgumentException     the illegal argument exception
2035      * @throws InvocationTargetException    the invocation target exception
2036      * @throws NoSuchMethodException        the no such method exception
2037      * @throws SecurityException            the security exception
2038      * @throws UnsupportedEncodingException the unsupported encoding exception
2039      * @throws AAIException                 the AAI exception
2040      * @throws MalformedURLException        the malformed URL exception
2041      * @throws URISyntaxException
2042      */
2043     private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException {
2044         List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2045
2046         Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2047             String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2048             return filterNodes.parallelStream().anyMatch(node::contains);
2049         }).iterator();
2050
2051
2052         List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices);
2053
2054         //items.parallelStream().anyMatch(inputStr::contains)
2055         List<Object> relationshipObjList = obj.getValue("relationship");
2056         for (Vertex cousin : cousins) {
2057
2058             Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
2059             Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
2060             if (result != null) {
2061                 relationshipObjList.add(result);
2062             }
2063
2064
2065         }
2066
2067         if (relationshipObjList.isEmpty()) {
2068             return null;
2069         } else {
2070             return obj;
2071         }
2072     }
2073
2074 }