Update DBSerializer to properly deal
[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 org.apache.commons.collections.IteratorUtils;
27 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
29 import org.apache.tinkerpop.gremlin.structure.Direction;
30 import org.apache.tinkerpop.gremlin.structure.Edge;
31 import org.apache.tinkerpop.gremlin.structure.Vertex;
32 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
33 import org.janusgraph.core.SchemaViolationException;
34 import org.javatuples.Triplet;
35 import org.onap.aai.concurrent.AaiCallable;
36 import org.onap.aai.config.SpringContextAware;
37 import org.onap.aai.db.props.AAIProperties;
38 import org.onap.aai.edges.EdgeIngestor;
39 import org.onap.aai.edges.EdgeRule;
40 import org.onap.aai.edges.EdgeRuleQuery;
41 import org.onap.aai.edges.TypeAlphabetizer;
42 import org.onap.aai.edges.enums.AAIDirection;
43 import org.onap.aai.edges.enums.EdgeField;
44 import org.onap.aai.edges.enums.EdgeProperty;
45 import org.onap.aai.edges.enums.EdgeType;
46 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
47 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
48 import org.onap.aai.exceptions.AAIException;
49 import org.onap.aai.introspection.*;
50 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
51 import org.onap.aai.introspection.sideeffect.*;
52 import org.onap.aai.logging.ErrorLogHelper;
53 import org.onap.aai.logging.LogFormatTools;
54 import org.onap.aai.logging.LoggingContext;
55 import org.onap.aai.logging.StopWatch;
56 import org.onap.aai.parsers.query.QueryParser;
57 import org.onap.aai.parsers.uri.URIParser;
58 import org.onap.aai.parsers.uri.URIToRelationshipObject;
59 import org.onap.aai.query.builder.QueryBuilder;
60 import org.onap.aai.schema.enums.ObjectMetadata;
61 import org.onap.aai.schema.enums.PropertyMetadata;
62 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
63 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
64 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
65 import org.onap.aai.serialization.engines.query.QueryEngine;
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.regex.Matcher;
85 import java.util.regex.Pattern;
86
87 public class DBSerializer {
88
89     private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
90
91     private static final String IMPLICIT_DELETE = "Implicit DELETE";
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 = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
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(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
421         // If the dependent vertices are not empty, then with
422         // the current behaviour, it should remove the vertices implicitly
423         // We are updating the code to properly log which call
424         // is doing this so the SE can work with the clients making the call to
425         // tell them not to call this API and can hopefully deprecate this
426         // functionality in the future releases
427         if(!dependentVertexes.isEmpty()){
428
429             LoggingContext.responseDescription(IMPLICIT_DELETE);
430
431             // Find all the deletable vertices from the dependent vertices that should be deleted
432             // So for each of the following dependent vertices,
433             // we will use the edge properties and do the cascade delete
434             List<Vertex> impliedDeleteVertices = this.engine.getQueryEngine().findDeletable(dependentVertexes);
435             int impliedDeleteCount = impliedDeleteVertices.size();
436
437             LOGGER.warn(
438                 "For the vertex with id {}, doing an implicit delete on update will delete total of {} vertexes",
439                 v.id(),
440                 impliedDeleteCount
441             );
442
443             String impliedDeleteLogEnabled = AAIConfig.get(AAIConstants.AAI_IMPLIED_DELETE_LOG_ENABLED, "true");
444
445             int impliedDeleteLogLimit = AAIConfig.getInt(AAIConstants.AAI_IMPLIED_DELETE_LOG_LIMIT, "-1");
446
447             if(impliedDeleteLogLimit == -1){
448                 impliedDeleteLogLimit = Integer.MAX_VALUE;
449             }
450
451             // If the logging is enabled for implied delete
452             // then log the payload in the latest format
453             if("true".equals(impliedDeleteLogEnabled) &&
454                 impliedDeleteCount <= impliedDeleteLogLimit){
455                 for(Vertex vertex : impliedDeleteVertices){
456                     Introspector introspector = null;
457                     try {
458                         introspector = getLatestVersionView(vertex);
459                         if(LOGGER.isInfoEnabled()){
460                             LOGGER.info("Implied delete object in json format {}", introspector.marshal(false));
461                         }
462                     } catch(Exception ex){
463                         LOGGER.warn("Encountered an exception during retrieval of vertex properties with vertex-id {} -> {}", v.id(), LogFormatTools.getStackTop(ex));
464                     }
465                 }
466             }
467
468             // After all the appropriate logging, calling the delete to delete the affected vertices
469             this.delete(impliedDeleteVertices);
470         }
471
472         this.executePostSideEffects(obj, v);
473         return processedVertexes;
474     }
475
476     /**
477      * Handle relationships.
478      *
479      * @param obj    the obj
480      * @param vertex the vertex
481      * @throws SecurityException            the security exception
482      * @throws IllegalAccessException       the illegal access exception
483      * @throws IllegalArgumentException     the illegal argument exception
484      * @throws InvocationTargetException    the invocation target exception
485      * @throws UnsupportedEncodingException the unsupported encoding exception
486      * @throws AAIException                 the AAI exception
487      */
488     /*
489      * Handles the explicit relationships defined for an obj
490      */
491     private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
492
493
494         Introspector wrappedRl = obj.getWrappedValue("relationship-list");
495         processRelationshipList(wrappedRl, vertex);
496
497
498     }
499
500
501     /**
502      * Process relationship list.
503      *
504      * @param wrapped the wrapped
505      * @param v       the v
506      * @throws UnsupportedEncodingException the unsupported encoding exception
507      * @throws AAIException                 the AAI exception
508      */
509     private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
510
511         List<Object> relationships = (List<Object>) wrapped.getValue("relationship");
512
513         List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
514         List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
515
516         for (Object relationship : relationships) {
517             Edge e = null;
518             Vertex cousinVertex = null;
519             String label = null;
520             Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
521             QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
522
523             if (wrappedRel.hasProperty("relationship-label")) {
524                 label = wrappedRel.getValue("relationship-label");
525             }
526
527             List<Vertex> results = parser.getQueryBuilder().toList();
528             if (results.isEmpty()) {
529                 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
530                 ex.getTemplateVars().add(parser.getResultType());
531                 ex.getTemplateVars().add(parser.getUri().toString());
532                 throw ex;
533             } else {
534                 //still an issue if there's more than one
535                 cousinVertex = results.get(0);
536             }
537
538             if (cousinVertex != null) {
539                 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
540                 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
541                 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
542
543
544                 if (!edgeRules.hasRule(baseQ.build())) {
545                     throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
546                         + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label) : "") + ".");
547                 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
548                     throw new AAIException("AAI_6145");
549                 }
550
551                 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
552
553                 if (e == null) {
554                     addEdges.add(new Triplet<>(v, cousinVertex, label));
555                 } else {
556                     existingEdges.remove(e);
557                 }
558             }
559         }
560
561         for (Edge edge : existingEdges) {
562             edge.remove();
563         }
564         for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
565             try {
566                 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
567             } catch (NoEdgeRuleFoundException e) {
568                 throw new AAIException("AAI_6129", e);
569             }
570         }
571
572     }
573
574     /**
575      * Write through defaults.
576      *
577      * @param v   the v
578      * @param obj the obj
579      * @throws AAIUnknownObjectException
580      */
581     private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
582         Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
583         if (latest != null) {
584             Set<String> required = latest.getRequiredProperties();
585
586             for (String field : required) {
587                 String defaultValue = null;
588                 Object vertexProp = null;
589                 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
590                 if (defaultValue != null) {
591                     vertexProp = v.<Object>property(field).orElse(null);
592                     if (vertexProp == null) {
593                         v.property(field, defaultValue);
594                     }
595                 }
596             }
597         }
598
599     }
600
601
602     /**
603      * Reflect dependent vertex.
604      *
605      * @param v            the v
606      * @param dependentObj the dependent obj
607      * @return the vertex
608      * @throws IllegalAccessException       the illegal access exception
609      * @throws IllegalArgumentException     the illegal argument exception
610      * @throws InvocationTargetException    the invocation target exception
611      * @throws InstantiationException       the instantiation exception
612      * @throws NoSuchMethodException        the no such method exception
613      * @throws SecurityException            the security exception
614      * @throws AAIException                 the AAI exception
615      * @throws UnsupportedEncodingException the unsupported encoding exception
616      * @throws AAIUnknownObjectException
617      */
618     private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
619
620         //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
621         //List<Vertex> items = p.getQuery().toList();
622         QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
623         query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
624         query.createKeyQuery(dependentObj);
625
626         List<Vertex> items = query.toList();
627
628         Vertex dependentVertex = null;
629         if (items.size() == 1) {
630             dependentVertex = items.get(0);
631             this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
632         } else {
633             this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
634             dependentVertex = createNewVertex(dependentObj);
635         }
636
637         return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
638
639     }
640
641     /**
642      * Reflect dependent vertex.
643      *
644      * @param parent the parent
645      * @param child  the child
646      * @param obj    the obj
647      * @return the vertex
648      * @throws IllegalAccessException       the illegal access exception
649      * @throws IllegalArgumentException     the illegal argument exception
650      * @throws InvocationTargetException    the invocation target exception
651      * @throws InstantiationException       the instantiation exception
652      * @throws NoSuchMethodException        the no such method exception
653      * @throws SecurityException            the security exception
654      * @throws AAIException                 the AAI exception
655      * @throws UnsupportedEncodingException the unsupported encoding exception
656      * @throws AAIUnknownObjectException
657      */
658     private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
659
660         String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
661         if (parentUri != null) {
662             String uri;
663             uri = obj.getURI();
664             addUriIfNeeded(child, parentUri + uri);
665         }
666         processObject(obj, child, requestContext);
667
668         Edge e;
669         e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
670
671         if (e == null) {
672             String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
673             if (canBeLinked != null && canBeLinked.equals("true")) {
674                 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext));
675                 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
676                 if (isFirst) {
677                     child.property(AAIProperties.LINKED, true);
678                 }
679             }
680             edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
681         }
682         return child;
683
684     }
685
686     private SchemaVersion getVerForContext(String requestContext) {
687         Pattern pattern = Pattern.compile("v[0-9]+");
688         Matcher m = pattern.matcher(requestContext);
689         if (!m.find()) {
690             return this.version;
691         } else {
692             return new SchemaVersion(requestContext);
693         }
694     }
695
696     /**
697      * Db to object.
698      *
699      * @param vertices the vertices
700      * @param obj      the obj
701      * @param depth    the depth
702      * @param cleanUp  the clean up
703      * @return the introspector
704      * @throws AAIException                 the AAI exception
705      * @throws IllegalAccessException       the illegal access exception
706      * @throws IllegalArgumentException     the illegal argument exception
707      * @throws InvocationTargetException    the invocation target exception
708      * @throws SecurityException            the security exception
709      * @throws InstantiationException       the instantiation exception
710      * @throws NoSuchMethodException        the no such method exception
711      * @throws UnsupportedEncodingException the unsupported encoding exception
712      * @throws MalformedURLException        the malformed URL exception
713      * @throws AAIUnknownObjectException
714      * @throws URISyntaxException
715      */
716     public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
717         final int internalDepth;
718         if (depth == Integer.MAX_VALUE) {
719             internalDepth = depth--;
720         } else {
721             internalDepth = depth;
722         }
723         StopWatch.conditionalStart();
724         if (vertices.size() > 1 && !obj.isContainer()) {
725             dbTimeMsecs += StopWatch.stopIfStarted();
726             throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
727         } else if (obj.isContainer()) {
728             final List getList;
729             String listProperty = null;
730             for (String property : obj.getProperties()) {
731                 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
732                     listProperty = property;
733                     break;
734                 }
735             }
736             final String propertyName = listProperty;
737             getList = (List) obj.getValue(listProperty);
738
739             /* This is an experimental multithreading experiment
740              * on get alls.
741              */
742             ExecutorService pool = GetAllPool.getInstance().getPool();
743
744             List<Future<Object>> futures = new ArrayList<>();
745
746             QueryEngine tgEngine = this.engine.getQueryEngine();
747             for (Vertex v : vertices) {
748
749                 AaiCallable<Object> task = new AaiCallable<Object>() {
750                     @Override
751                     public Object process() throws UnsupportedEncodingException, AAIException {
752                         Set<Vertex> seen = new HashSet<>();
753                         Introspector childObject;
754                         try {
755                             childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
756                         } catch (AAIUnknownObjectException e) {
757                             throw e;
758                         }
759                         try {
760                             dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp);
761                         } catch (UnsupportedEncodingException e) {
762                             throw e;
763                         } catch (AAIException e) {
764                             throw e;
765                         }
766                         return childObject.getUnderlyingObject();
767                         //getList.add(childObject.getUnderlyingObject());
768                     }
769                 };
770                 futures.add(pool.submit(task));
771             }
772
773             for (Future<Object> future : futures) {
774                 try {
775                     getList.add(future.get());
776                 } catch (ExecutionException e) {
777                     dbTimeMsecs += StopWatch.stopIfStarted();
778                     throw new AAIException("AAI_4000", e);
779                 } catch (InterruptedException e) {
780                     dbTimeMsecs += StopWatch.stopIfStarted();
781                     throw new AAIException("AAI_4000", e);
782                 }
783             }
784         } else if (vertices.size() == 1) {
785             Set<Vertex> seen = new HashSet<>();
786             dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp);
787         } else {
788             //obj = null;
789         }
790
791         dbTimeMsecs += StopWatch.stopIfStarted();
792         return obj;
793     }
794
795     /**
796      * Db to object.
797      *
798      * @param obj     the obj
799      * @param v       the v
800      * @param seen    the seen
801      * @param depth   the depth
802      * @param cleanUp the clean up
803      * @return the introspector
804      * @throws IllegalAccessException       the illegal access exception
805      * @throws IllegalArgumentException     the illegal argument exception
806      * @throws InvocationTargetException    the invocation target exception
807      * @throws SecurityException            the security exception
808      * @throws InstantiationException       the instantiation exception
809      * @throws NoSuchMethodException        the no such method exception
810      * @throws UnsupportedEncodingException the unsupported encoding exception
811      * @throws AAIException                 the AAI exception
812      * @throws MalformedURLException        the malformed URL exception
813      * @throws AAIUnknownObjectException
814      * @throws URISyntaxException
815      */
816     private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
817
818         if (depth < 0) {
819             return null;
820         }
821         depth--;
822         seen.add(v);
823
824         boolean modified = false;
825         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
826             List<Object> getList = null;
827             Vertex[] vertices = null;
828
829             if (!(obj.isComplexType(property) || obj.isListType(property))) {
830                 this.copySimpleProperty(property, obj, v);
831                 modified = true;
832             } else {
833                 if (obj.isComplexType(property)) {
834                     /* container case */
835
836                     if (!property.equals("relationship-list") && depth >= 0) {
837                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
838                         Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
839                         if (result != null) {
840                             obj.setValue(property, argumentObject.getUnderlyingObject());
841                             modified = true;
842                         }
843                     } else if (property.equals("relationship-list") && !nodeOnly) {
844                         /* relationships need to be handled correctly */
845                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
846                         relationshipList = createRelationshipList(v, relationshipList, cleanUp);
847                         if (relationshipList != null) {
848                             modified = true;
849                             obj.setValue(property, relationshipList.getUnderlyingObject());
850                             modified = true;
851                         }
852
853                     }
854                 } else if (obj.isListType(property)) {
855
856                     if (property.equals("any")) {
857                         continue;
858                     }
859                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
860                     if (obj.isComplexGenericType(property) && depth >= 0) {
861                         final String childDbName = convertFromCamelCase(genericType);
862                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
863                         EdgeRule rule;
864
865                         try {
866                             rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
867                         } catch (EdgeRuleNotFoundException e) {
868                             throw new NoEdgeRuleFoundException(e);
869                         } catch (AmbiguousRuleChoiceException e) {
870                             throw new MultipleEdgeRuleFoundException(e);
871                         }
872                         if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
873                             //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
874                             Direction ruleDirection = rule.getDirection();
875                             Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
876                             List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
877                             itr = verticesList.stream().filter(item -> {
878                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
879                             }).iterator();
880                             if (itr.hasNext()) {
881                                 getList = (List<Object>) obj.getValue(property);
882                             }
883                             int processed = 0;
884                             int removed = 0;
885                             while (itr.hasNext()) {
886                                 Vertex childVertex = itr.next();
887                                 if (!seen.contains(childVertex)) {
888                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
889
890                                     Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
891                                     if (result != null) {
892                                         getList.add(argumentObject.getUnderlyingObject());
893                                     }
894
895                                     processed++;
896                                 } else {
897                                     removed++;
898                                     LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
899                                 }
900                             }
901                             if (processed == 0) {
902                                 //vertices were all seen, reset the list
903                                 getList = null;
904                             }
905                             if (processed > 0) {
906                                 modified = true;
907                             }
908                         }
909                     } else if (obj.isSimpleGenericType(property)) {
910                         List<Object> temp = this.engine.getListProperty(v, property);
911                         if (temp != null) {
912                             getList = (List<Object>) obj.getValue(property);
913                             getList.addAll(temp);
914                             modified = true;
915                         }
916
917                     }
918
919                 }
920
921             }
922         }
923
924         //no changes were made to this obj, discard the instance
925         if (!modified) {
926             return null;
927         }
928         this.enrichData(obj, v);
929         return obj;
930
931     }
932
933
934     public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
935         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
936         if (nodeType == null) {
937             throw new AAIException("AAI_6143");
938         }
939
940         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
941         Set<Vertex> seen = new HashSet<>();
942         int depth = 0;
943         String cleanUp = "false";
944         boolean nodeOnly = true;
945         StopWatch.conditionalStart();
946         this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
947         dbTimeMsecs += StopWatch.stopIfStarted();
948         return obj;
949
950     }
951
952     public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
953         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
954         if (nodeType == null) {
955             throw new AAIException("AAI_6143");
956         }
957         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
958         Set<Vertex> seen = new HashSet<>();
959         int depth = AAIProperties.MAXIMUM_DEPTH;
960         String cleanUp = "false";
961         boolean nodeOnly = false;
962         StopWatch.conditionalStart();
963         this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
964         dbTimeMsecs += StopWatch.stopIfStarted();
965         return obj;
966     }
967
968     /**
969      * Copy simple property.
970      *
971      * @param property the property
972      * @param obj      the obj
973      * @param v        the v
974      * @throws InstantiationException    the instantiation exception
975      * @throws IllegalAccessException    the illegal access exception
976      * @throws IllegalArgumentException  the illegal argument exception
977      * @throws InvocationTargetException the invocation target exception
978      * @throws NoSuchMethodException     the no such method exception
979      * @throws SecurityException         the security exception
980      */
981     private void copySimpleProperty(String property, Introspector obj, Vertex v) {
982         final Object temp = getProperty(obj, property, v);
983         if (temp != null) {
984             obj.setValue(property, temp);
985         }
986     }
987
988
989     /**
990      * Load the introspector from the hashmap for the given property key
991      *
992      * @param property - vertex property
993      * @param obj      - introspector object representing the vertex
994      * @param hashMap  - Containing a list of pre-fetched properties for a given vertex
995      */
996     private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap){
997
998         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
999         String dbPropertyName = property;
1000
1001         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1002             dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1003         }
1004
1005         final Object temp = hashMap.getOrDefault(dbPropertyName, null);
1006
1007         if (temp != null) {
1008             obj.setValue(property, temp);
1009         }
1010     }
1011
1012     /**
1013      * Simple db to object.
1014      *
1015      * @param obj the obj
1016      * @param v   the v
1017      * @throws InstantiationException    the instantiation exception
1018      * @throws IllegalAccessException    the illegal access exception
1019      * @throws IllegalArgumentException  the illegal argument exception
1020      * @throws InvocationTargetException the invocation target exception
1021      * @throws NoSuchMethodException     the no such method exception
1022      * @throws SecurityException         the security exception
1023      */
1024     private void simpleDbToObject(Introspector obj, Vertex v) {
1025         for(String key : obj.getProperties()){
1026             this.copySimpleProperty(key, obj, v);
1027         }
1028     }
1029
1030
1031     public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v){
1032
1033         long startTime = System.currentTimeMillis();
1034
1035         Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1036         String[] simplePropsArray    = new String[simpleProperties.size()];
1037         simplePropsArray             = simpleProperties.toArray(simplePropsArray);
1038
1039         Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1040
1041         v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value()));
1042
1043         return simplePropsHashMap;
1044     }
1045
1046     public Introspector dbToRelationshipObject(Vertex v) throws UnsupportedEncodingException, AAIException {
1047         Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1048         relationshipList =  createRelationshipList(v, relationshipList, "false");
1049         return relationshipList;
1050     }
1051     /**
1052      * Creates the relationship list.
1053      *
1054      * @param v       the v
1055      * @param obj     the obj
1056      * @param cleanUp the clean up
1057      * @return the object
1058      * @throws InstantiationException       the instantiation exception
1059      * @throws IllegalAccessException       the illegal access exception
1060      * @throws IllegalArgumentException     the illegal argument exception
1061      * @throws InvocationTargetException    the invocation target exception
1062      * @throws NoSuchMethodException        the no such method exception
1063      * @throws SecurityException            the security exception
1064      * @throws UnsupportedEncodingException the unsupported encoding exception
1065      * @throws AAIException                 the AAI exception
1066      * @throws MalformedURLException        the malformed URL exception
1067      * @throws URISyntaxException
1068      */
1069     private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
1070
1071         String[] cousinRules = new String[0];
1072
1073         try {
1074             cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName());
1075         } catch (ExecutionException e) {
1076             LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", obj.getDbName(), e);
1077         }
1078
1079         List<Vertex> cousins = null;
1080         if(cousinRules != null && cousinRules.length != 0){
1081             cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules);
1082         } else {
1083             cousins = this.engine.getQueryEngine().findCousinVertices(v);
1084         }
1085
1086         List<Object> relationshipObjList = obj.getValue("relationship");
1087         VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1088
1089         if(!nodeTypeProperty.isPresent()){
1090             LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY);
1091             LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1092             LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString());
1093             return null;
1094         }
1095
1096         String aNodeType = nodeTypeProperty.value().toString();
1097
1098         TypeAlphabetizer alphabetizer = new TypeAlphabetizer();
1099
1100         EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1101         Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys();
1102
1103         // For the given vertex, find all the cousins
1104         // For each cousin retrieve the node type and then
1105         // check if the version is greater than the edge label version
1106         // meaning is the current version equal to greater than the version
1107         // where we introduced the edge labels into the relationship payload
1108         // If it is, then we check if the edge key there are multiple labels
1109         // If there are multiple labels, then we need to go to the database
1110         // to retrieve the labels between itself and cousin vertex
1111         // If there is only single label between the edge a and b, then
1112         // we can retrieve what that is without going to the database
1113         // from using the edge rules json and get the edge rule out of it
1114         EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType);
1115         for (Vertex cousin : cousins) {
1116             VertexProperty vertexProperty = cousin.property(AAIProperties.NODE_TYPE);
1117             String bNodeType = null;
1118             if(vertexProperty.isPresent()){
1119                 bNodeType = cousin.property(AAIProperties.NODE_TYPE).value().toString();
1120             } else {
1121                 // If the vertex is missing the aai-node-type
1122                 // Then its either a bad vertex or its in the process
1123                 // of getting deleted so we should ignore these vertexes
1124                 LoggingContext.responseDescription(MISSING_REQUIRED_NODE_PROPERTY);
1125                 if(LOGGER.isDebugEnabled()){
1126                     LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString());
1127                 } else {
1128                     LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log");
1129                 }
1130                 LoggingContext.remove(LoggingContext.LoggingField.RESPONSE_DESCRIPTION.toString());
1131                 continue;
1132             }
1133             if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) {
1134                 String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType);
1135                 if(keysWithMultipleLabels.contains(edgeKey)){
1136                     List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin);
1137                     for(String edgeLabel: edgeLabels){
1138                         Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1139                         Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel);
1140                         if (result != null) {
1141                             relationshipObjList.add(result);
1142                         }
1143                     }
1144                 } else {
1145
1146                     EdgeRule edgeRule = null;
1147
1148                     // Create a query based on the a nodetype and b nodetype
1149                     // which is also a cousin edge and ensure the version
1150                     // is used properly so for example in order to be backwards
1151                     // compatible if we had allowed a edge between a and b
1152                     // in a previous release and we decided to remove it from
1153                     // the edge rules in the future we can display the edge
1154                     // only for the older apis and the new apis if the edge rule
1155                     // is removed will not be seen in the newer version of the API
1156
1157                     EdgeRuleQuery ruleQuery = queryBuilder
1158                         .to(bNodeType)
1159                         .edgeType(EdgeType.COUSIN)
1160                         .version(obj.getVersion())
1161                         .build();
1162
1163                     try {
1164                         edgeRule = edgeIngestor.getRule(ruleQuery);
1165                     } catch (EdgeRuleNotFoundException e) {
1166                         LOGGER.warn("Caught an edge rule not found exception for query {}, {}," +
1167                             " it could be the edge rule is no longer valid for the existing edge in db",
1168                             ruleQuery, LogFormatTools.getStackTop(e));
1169                         continue;
1170                     } catch (AmbiguousRuleChoiceException e) {
1171                         LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}",
1172                             ruleQuery, LogFormatTools.getStackTop(e));
1173                         continue;
1174                     }
1175
1176                     Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1177                     Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp,edgeRule.getLabel());
1178                     if (result != null) {
1179                         relationshipObjList.add(result);
1180                     }
1181                 }
1182             } else {
1183                 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1184                 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1185                 if (result != null) {
1186                     relationshipObjList.add(result);
1187                 }
1188             }
1189
1190         }
1191
1192         if (relationshipObjList.isEmpty()) {
1193             return null;
1194         } else {
1195             return obj;
1196         }
1197     }
1198
1199     /**
1200      * Process edge relationship.
1201      *
1202      * @param relationshipObj the relationship obj
1203      * @param edge            the edge
1204      * @param cleanUp         the clean up
1205      * @return the object
1206      * @throws InstantiationException       the instantiation exception
1207      * @throws IllegalAccessException       the illegal access exception
1208      * @throws IllegalArgumentException     the illegal argument exception
1209      * @throws InvocationTargetException    the invocation target exception
1210      * @throws NoSuchMethodException        the no such method exception
1211      * @throws SecurityException            the security exception
1212      * @throws UnsupportedEncodingException the unsupported encoding exception
1213      * @throws AAIException                 the AAI exception
1214      * @throws MalformedURLException        the malformed URL exception
1215      * @throws AAIUnknownObjectException
1216      * @throws URISyntaxException
1217      */
1218     private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException {
1219
1220         VertexProperty aaiUriProperty = cousin.property("aai-uri");
1221
1222         if(!aaiUriProperty.isPresent()){
1223             return null;
1224         }
1225
1226         URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1227
1228         URIToRelationshipObject uriParser = null;
1229         Introspector result = null;
1230         try {
1231             uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1232             result = uriParser.getResult();
1233         } catch (AAIException | URISyntaxException e) {
1234             LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + ": "
1235                 + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1236             return null;
1237         }
1238
1239         VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1240
1241         if(cousinVertexNodeType.isPresent()){
1242             String cousinType = cousinVertexNodeType.value().toString();
1243             if(namedPropNodes.contains(cousinType)){
1244                 this.addRelatedToProperty(result, cousin, cousinType);
1245             }
1246         }
1247
1248         if (edgeLabel != null && result.hasProperty("relationship-label")) {
1249             result.setValue("relationship-label", edgeLabel);
1250         }
1251
1252         return result.getUnderlyingObject();
1253     }
1254
1255     /**
1256      * Gets the URI for vertex.
1257      *
1258      * @param v the v
1259      * @return the URI for vertex
1260      * @throws InstantiationException       the instantiation exception
1261      * @throws IllegalAccessException       the illegal access exception
1262      * @throws IllegalArgumentException     the illegal argument exception
1263      * @throws InvocationTargetException    the invocation target exception
1264      * @throws NoSuchMethodException        the no such method exception
1265      * @throws SecurityException            the security exception
1266      * @throws UnsupportedEncodingException the unsupported encoding exception
1267      * @throws AAIUnknownObjectException
1268      */
1269     public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1270
1271         return getURIForVertex(v, false);
1272     }
1273
1274     public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
1275         URI uri = UriBuilder.fromPath("/unknown-uri").build();
1276
1277         String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1278
1279         if (aaiUri != null && !overwrite) {
1280             uri = UriBuilder.fromPath(aaiUri).build();
1281         }
1282
1283         return uri;
1284     }
1285
1286     /**
1287      * Gets the URI from list.
1288      *
1289      * @param list the list
1290      * @return the URI from list
1291      * @throws UnsupportedEncodingException the unsupported encoding exception
1292      */
1293     private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1294         String uri = "";
1295         StringBuilder sb = new StringBuilder();
1296         for (Introspector i : list) {
1297             sb.insert(0, i.getURI());
1298         }
1299
1300         uri = sb.toString();
1301         return UriBuilder.fromPath(uri).build();
1302     }
1303
1304     public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType) throws AAIUnknownObjectException {
1305
1306         Introspector obj = null;
1307
1308         try {
1309             obj = this.loader.introspectorFromName(cousinType);
1310         } catch(AAIUnknownObjectException ex){
1311             if(LOGGER.isTraceEnabled()){
1312                 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}", cousinType, cousinVertex.id());
1313             }
1314             return;
1315         }
1316
1317         String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1318         List<Introspector> relatedToProperties = new ArrayList<>();
1319
1320         if (nameProps != null) {
1321             String[] props = nameProps.split(",");
1322             for (String prop : props) {
1323                 final Object temp = getProperty(obj, prop, cousinVertex);
1324                 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1325                 relatedTo.setValue("property-key", cousinType + "." + prop);
1326                 relatedTo.setValue("property-value", temp);
1327                 relatedToProperties.add(relatedTo);
1328             }
1329         }
1330
1331         if (!relatedToProperties.isEmpty()) {
1332             List relatedToList = (List) relationship.getValue("related-to-property");
1333             for (Introspector introspector : relatedToProperties) {
1334                 relatedToList.add(introspector.getUnderlyingObject());
1335             }
1336         }
1337
1338     }
1339
1340     private Object getProperty(Introspector obj, String prop, Vertex vertex){
1341
1342         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1343         String dbPropertyName = prop;
1344
1345         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1346             dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1347         }
1348
1349         return vertex.<Object>property(dbPropertyName).orElse(null);
1350     }
1351
1352     /**
1353      * Creates the edge.
1354      *
1355      * @param relationship the relationship
1356      * @param inputVertex  the input vertex
1357      * @return true, if successful
1358      * @throws UnsupportedEncodingException the unsupported encoding exception
1359      * @throws AAIException                 the AAI exception
1360      */
1361     public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1362
1363         Vertex relatedVertex = null;
1364         StopWatch.conditionalStart();
1365         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1366
1367         String label = null;
1368         if (relationship.hasProperty("relationship-label")) {
1369             label = relationship.getValue("relationship-label");
1370         }
1371
1372         List<Vertex> results = parser.getQueryBuilder().toList();
1373         if (results.isEmpty()) {
1374             dbTimeMsecs += StopWatch.stopIfStarted();
1375             AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1376             e.getTemplateVars().add(parser.getResultType());
1377             e.getTemplateVars().add(parser.getUri().toString());
1378             throw e;
1379         } else {
1380             //still an issue if there's more than one
1381             relatedVertex = results.get(0);
1382         }
1383
1384         if (relatedVertex != null) {
1385
1386             Edge e;
1387             try {
1388                 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1389                 if (e == null) {
1390                     edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1391                 } else {
1392                     //attempted to link two vertexes already linked
1393                 }
1394             } finally {
1395                 dbTimeMsecs += StopWatch.stopIfStarted();
1396             }
1397         }
1398
1399         dbTimeMsecs += StopWatch.stopIfStarted();
1400         return true;
1401     }
1402
1403     /**
1404      * Gets all the edges between of the type with the specified label.
1405      *
1406      * @param aVertex the out vertex
1407      * @param bVertex the in vertex
1408      * @return the edges between
1409      * @throws AAIException             the AAI exception
1410      * @throws NoEdgeRuleFoundException
1411      */
1412     private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1413
1414         Edge result = null;
1415
1416         if (bVertex != null) {
1417             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1418             if (EdgeType.TREE.equals(type)) {
1419                 GraphTraversal<Vertex,Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1420                 if(edgeRule.getDirection().equals(Direction.IN)){
1421                     findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1422                         .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1423                         .not(
1424                             __.has(EdgeField.PRIVATE.toString(), true)
1425                         );
1426                 } else {
1427                     findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1428                         .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1429                         .not(
1430                             __.has(EdgeField.PRIVATE.toString(), true)
1431                         );
1432                 }
1433                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1434             } else {
1435                 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1436                 findEdgesBetween = findEdgesBetween
1437                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1438                     .not(
1439                         __.has(EdgeField.PRIVATE.toString(), true)
1440                     );
1441                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1442             }
1443             List<Edge> list = findEdgesBetween.toList();
1444             if(!list.isEmpty()){
1445                 result = list.get(0);
1446             }
1447         }
1448
1449         return result;
1450     }
1451
1452     /**
1453      * Gets all the edges between of the type.
1454      *
1455      * @param aVertex the out vertex
1456      * @param bVertex the in vertex
1457      * @return the edges between
1458      * @throws AAIException             the AAI exception
1459      * @throws NoEdgeRuleFoundException
1460      */
1461     private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1462
1463         List<Edge> result = new ArrayList<>();
1464
1465         if (bVertex != null) {
1466             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1467             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1468             if (EdgeType.TREE.equals(type)) {
1469                 findEdgesBetween = findEdgesBetween
1470                     .not(
1471                         __.or(
1472                             __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1473                             __.has(EdgeField.PRIVATE.toString(), true)
1474                         )
1475                     );
1476             } else {
1477                 findEdgesBetween = findEdgesBetween
1478                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1479                     .not(
1480                         __.has(EdgeField.PRIVATE.toString(), true)
1481                     );
1482             }
1483             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1484             result = findEdgesBetween.toList();
1485         }
1486
1487         return result;
1488     }
1489
1490     /**
1491      * Gets all the edges string between of the type.
1492      *
1493      * @param aVertex the out vertex
1494      * @param bVertex the in vertex
1495      * @return the edges between
1496      * @throws AAIException the AAI exception
1497      * @throws NoEdgeRuleFoundException
1498      */
1499     private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1500
1501         List<String> result = new ArrayList<>();
1502
1503         if (bVertex != null) {
1504             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1505             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1506             if (EdgeType.TREE.equals(type)) {
1507                 findEdgesBetween = findEdgesBetween
1508                         .not(
1509                                 __.or(
1510                                         __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1511                                         __.has(EdgeField.PRIVATE.toString(), true)
1512                                 )
1513                         );
1514             } else {
1515                 findEdgesBetween = findEdgesBetween
1516                         .has(EdgeProperty.CONTAINS.toString(), "NONE")
1517                         .not(
1518                                 __.has(EdgeField.PRIVATE.toString(), true)
1519                         );
1520             }
1521             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1522             result = findEdgesBetween.label().toList();
1523         }
1524         return result;
1525     }
1526
1527     /**
1528      * Gets all the edges string between of the type.
1529      *
1530      * @param aVertex the out vertex
1531      * @param bVertex the in vertex
1532      * @return the edges between
1533      * @throws AAIException the AAI exception
1534      * @throws NoEdgeRuleFoundException
1535      */
1536     private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) {
1537
1538         Long result = null;
1539
1540         if (bVertex != null) {
1541             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1542             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1543             findEdgesBetween = findEdgesBetween
1544                     .has(EdgeProperty.CONTAINS.toString(), "NONE")
1545                     .not(
1546                             __.has(EdgeField.PRIVATE.toString(), true)
1547                     );
1548             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1549             result = findEdgesBetween.count().next();
1550         }
1551         return result;
1552     }
1553     /**
1554      * Gets all the edges between the vertexes with the label and type.
1555      *
1556      * @param aVertex the out vertex
1557      * @param bVertex the in vertex
1558      * @param label
1559      * @return the edges between
1560      * @throws AAIException the AAI exception
1561      */
1562     private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1563
1564         Edge edge = null;
1565
1566         if (bVertex != null) {
1567             String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1568             String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1569             EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1570             EdgeRule rule;
1571             try {
1572                 rule = edgeRules.getRule(q);
1573             } catch (EdgeRuleNotFoundException e) {
1574                 throw new NoEdgeRuleFoundException(e);
1575             } catch (AmbiguousRuleChoiceException e) {
1576                 throw new MultipleEdgeRuleFoundException(e);
1577             }
1578             edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1579         }
1580
1581         return edge;
1582     }
1583
1584     /**
1585      * Gets the edge between with the label and edge type.
1586      *
1587      * @param aVertex the out vertex
1588      * @param bVertex the in vertex
1589      * @param label
1590      * @return the edge between
1591      * @throws AAIException             the AAI exception
1592      * @throws NoEdgeRuleFoundException
1593      */
1594     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1595
1596         StopWatch.conditionalStart();
1597         if (bVertex != null) {
1598
1599             Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1600             if (edge != null) {
1601                 dbTimeMsecs += StopWatch.stopIfStarted();
1602                 return edge;
1603             }
1604
1605         }
1606         dbTimeMsecs += StopWatch.stopIfStarted();
1607         return null;
1608     }
1609
1610     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1611         return this.getEdgeBetween(type, aVertex, bVertex, null);
1612     }
1613
1614
1615     /**
1616      * Delete edge.
1617      *
1618      * @param relationship the relationship
1619      * @param inputVertex  the input vertex
1620      * @return true, if successful
1621      * @throws UnsupportedEncodingException the unsupported encoding exception
1622      * @throws AAIException                 the AAI exception
1623      */
1624     public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1625
1626         Vertex relatedVertex = null;
1627         StopWatch.conditionalStart();
1628         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1629
1630         List<Vertex> results = parser.getQueryBuilder().toList();
1631
1632         String label = null;
1633         if (relationship.hasProperty("relationship-label")) {
1634             label = relationship.getValue("relationship-label");
1635         }
1636
1637         if (results.isEmpty()) {
1638             dbTimeMsecs += StopWatch.stopIfStarted();
1639             return false;
1640         }
1641
1642         relatedVertex = results.get(0);
1643         Edge edge;
1644         try {
1645             edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1646         } catch (NoEdgeRuleFoundException e) {
1647             dbTimeMsecs += StopWatch.stopIfStarted();
1648             throw new AAIException("AAI_6129", e);
1649         }
1650         if (edge != null) {
1651             edge.remove();
1652             dbTimeMsecs += StopWatch.stopIfStarted();
1653             return true;
1654         } else {
1655             dbTimeMsecs += StopWatch.stopIfStarted();
1656             return false;
1657         }
1658
1659     }
1660
1661     /**
1662      * Delete items with traversal.
1663      *
1664      * @param vertexes the vertexes
1665      * @throws IllegalStateException the illegal state exception
1666      */
1667     public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1668
1669         for (Vertex v : vertexes) {
1670             deleteWithTraversal(v);
1671         }
1672
1673     }
1674
1675     /**
1676      * Delete with traversal.
1677      *
1678      * @param startVertex the start vertex
1679      */
1680     public void deleteWithTraversal(Vertex startVertex) {
1681         StopWatch.conditionalStart();
1682         List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1683
1684         for (Vertex v : results) {
1685             LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
1686             v.remove();
1687         }
1688         dbTimeMsecs += StopWatch.stopIfStarted();
1689     }
1690
1691     /**
1692      * Removes the list of vertexes from the graph
1693      * <p>
1694      * Current the vertex label will just be vertex but
1695      * in the future the aai-node-type property will be replaced
1696      * by using the vertex label as when retrieving an vertex
1697      * and retrieving an single property on an vertex will pre-fetch
1698      * all the properties of that vertex and this is due to the following property
1699      * <p>
1700      * <code>
1701      * query.fast-property=true
1702      * </code>
1703      * <p>
1704      * JanusGraph doesn't provide the capability to override that
1705      * at a transaction level and there is a plan to move to vertex label
1706      * so it is best to utilize this for now and when the change is applied
1707      *
1708      * @param vertices - list of vertices to delete from the graph
1709      */
1710     void delete(List<Vertex> vertices){
1711         StopWatch.conditionalStart();
1712
1713         for (Vertex v : vertices) {
1714             LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
1715             v.remove();
1716         }
1717
1718         dbTimeMsecs += StopWatch.stopIfStarted();
1719     }
1720
1721     /**
1722      * Delete.
1723      *
1724      * @param v               the v
1725      * @param resourceVersion the resource version
1726      * @throws IllegalArgumentException the illegal argument exception
1727      * @throws AAIException             the AAI exception
1728      * @throws InterruptedException     the interrupted exception
1729      */
1730     public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1731
1732         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1733         /*
1734          * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1735          * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted
1736          * So its better to make these in 2 steps
1737          */
1738         if (result && !deletableVertices.isEmpty()) {
1739             result = verifyPreventDeleteSemantics(deletableVertices);
1740         }
1741         if (result) {
1742
1743             try {
1744                 deleteWithTraversal(v);
1745             } catch (IllegalStateException e) {
1746                 throw new AAIException("AAI_6110", e);
1747             }
1748
1749         }
1750
1751     }
1752
1753
1754     /**
1755      * Delete.
1756      *
1757      * @param v               the v
1758      * @param resourceVersion the resource version
1759      * @throws IllegalArgumentException the illegal argument exception
1760      * @throws AAIException             the AAI exception
1761      * @throws InterruptedException     the interrupted exception
1762      */
1763     public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1764
1765         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1766
1767         if (result) {
1768
1769             try {
1770                 deleteWithTraversal(v);
1771             } catch (IllegalStateException e) {
1772                 throw new AAIException("AAI_6110", e);
1773             }
1774
1775         }
1776
1777     }
1778
1779     /**
1780      * Verify delete semantics.
1781      *
1782      * @param vertex          the vertex
1783      * @param resourceVersion the resource version
1784      * @return true, if successful
1785      * @throws AAIException the AAI exception
1786      */
1787     private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1788         boolean result = true;
1789         String nodeType = "";
1790         String errorDetail = " unknown delete semantic found";
1791         String aaiExceptionCode = "";
1792         nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1793         if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1794         }
1795         List<Vertex> vertices = new ArrayList<Vertex>();
1796         vertices.add(vertex);
1797         result = verifyPreventDeleteSemantics(vertices);
1798
1799         return result;
1800     }
1801
1802     /**
1803      * Verify Prevent delete semantics.
1804      *
1805      * @param vertices the list of vertices
1806      * @return true, if successful
1807      * @throws AAIException the AAI exception
1808      */
1809     private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
1810         boolean result = true;
1811         String nodeType = "";
1812         String errorDetail = " unknown delete semantic found";
1813         String aaiExceptionCode = "";
1814
1815         StopWatch.conditionalStart();
1816         /*
1817          * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition
1818          * If yes - that should prevent the deletion of the vertex
1819          * Dedup makes sure we dont capture the prevent-delete vertices twice
1820          * The prevent-delete vertices are stored so that the error message displays what prevents the delete
1821          */
1822
1823         List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices).
1824             union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE),
1825                 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE))
1826             .dedup().toList();
1827
1828         dbTimeMsecs += StopWatch.stopIfStarted();
1829         if (!preventDeleteVertices.isEmpty()) {
1830             aaiExceptionCode = "AAI_6110";
1831             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);
1832             result = false;
1833         }
1834         if (!result) {
1835             throw new AAIException(aaiExceptionCode, errorDetail);
1836         }
1837         return result;
1838     }
1839
1840     /**
1841      * Verify resource version.
1842      *
1843      * @param action                 the action
1844      * @param nodeType               the node type
1845      * @param currentResourceVersion the current resource version
1846      * @param resourceVersion        the resource version
1847      * @param uri                    the uri
1848      * @return true, if successful
1849      * @throws AAIException the AAI exception
1850      */
1851     public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1852         String enabled = "";
1853         String errorDetail = "";
1854         String aaiExceptionCode = "";
1855         boolean isDeleteResourceVersionOk = true;
1856         if (currentResourceVersion == null) {
1857             currentResourceVersion = "";
1858         }
1859
1860         if (resourceVersion == null) {
1861             resourceVersion = "";
1862         }
1863         try {
1864             enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1865
1866         } catch (AAIException e) {
1867             ErrorLogHelper.logException(e);
1868         }
1869         if (enabled.equals("true")) {
1870             if ("delete".equals(action)) {
1871                 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
1872             }
1873             if ((!isDeleteResourceVersionOk) || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
1874                 if ("create".equals(action) && !resourceVersion.equals("")) {
1875                     errorDetail = "resource-version passed for " + action + " of " + uri;
1876                     aaiExceptionCode = "AAI_6135";
1877                 } else if (resourceVersion.equals("")) {
1878                     errorDetail = "resource-version not passed for " + action + " of " + uri;
1879                     aaiExceptionCode = "AAI_6130";
1880                 } else {
1881                     errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1882                     aaiExceptionCode = "AAI_6131";
1883                 }
1884
1885                 throw new AAIException(aaiExceptionCode, errorDetail);
1886
1887             }
1888         }
1889         return true;
1890     }
1891
1892     /**
1893      * Verify resource version for delete.
1894      *
1895      * @param currentResourceVersion the current resource version
1896      * @param resourceVersion        the resource version
1897      * @return true, if successful or false if there is a mismatch
1898      */
1899     private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
1900
1901         boolean isDeleteResourceVersionOk = true;
1902         String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
1903             AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
1904
1905         if ((!currentResourceVersion.equals(resourceVersion)) && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
1906             isDeleteResourceVersionOk = false;
1907         }
1908         return isDeleteResourceVersionOk;
1909     }
1910
1911     /**
1912      * Convert from camel case.
1913      *
1914      * @param name the name
1915      * @return the string
1916      */
1917     private String convertFromCamelCase(String name) {
1918         String result = "";
1919         result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1920
1921         NamingExceptions exceptions = NamingExceptions.getInstance();
1922         result = exceptions.getDBName(result);
1923
1924         return result;
1925     }
1926
1927     private boolean canModify(Introspector obj, String propName, String requestContext) {
1928         final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1929         if (readOnly != null) {
1930             final String[] items = readOnly.split(",");
1931             for (String item : items) {
1932                 if (requestContext.equals(item)) {
1933                     return false;
1934                 }
1935             }
1936         }
1937         return true;
1938     }
1939
1940     private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1941
1942         SideEffectRunner runner = new SideEffectRunner
1943             .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build();
1944
1945         runner.execute(obj, self);
1946     }
1947
1948     private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1949
1950         SideEffectRunner runner = new SideEffectRunner
1951             .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1952
1953         runner.execute(obj, self);
1954     }
1955
1956     private void enrichData(Introspector obj, Vertex self) throws AAIException {
1957
1958         SideEffectRunner runner = new SideEffectRunner
1959             .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1960
1961         runner.execute(obj, self);
1962     }
1963
1964     public double getDBTimeMsecs() {
1965         return (dbTimeMsecs);
1966     }
1967
1968     /**
1969      * Db to object With Filters
1970      * This is for a one-time run with Tenant Isloation to only filter relationships
1971      * TODO: Chnage the original dbToObject to take filter parent/cousins
1972      *
1973      * @param obj               the obj
1974      * @param v                 the vertex from the graph
1975      * @param depth             the depth
1976      * @param nodeOnly          specify if to exclude relationships or not
1977      * @param filterCousinNodes
1978      * @return the introspector
1979      * @throws AAIException                 the AAI exception
1980      * @throws IllegalAccessException       the illegal access exception
1981      * @throws IllegalArgumentException     the illegal argument exception
1982      * @throws InvocationTargetException    the invocation target exception
1983      * @throws SecurityException            the security exception
1984      * @throws InstantiationException       the instantiation exception
1985      * @throws NoSuchMethodException        the no such method exception
1986      * @throws UnsupportedEncodingException the unsupported encoding exception
1987      * @throws MalformedURLException        the malformed URL exception
1988      * @throws AAIUnknownObjectException
1989      * @throws URISyntaxException
1990      */
1991     //TODO - See if you can merge the 2 dbToObjectWithFilters
1992     public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException {
1993         String cleanUp = "false";
1994         if (depth < 0) {
1995             return null;
1996         }
1997         depth--;
1998         seen.add(v);
1999         boolean modified = false;
2000         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2001             List<Object> getList = null;
2002             Vertex[] vertices = null;
2003
2004             if (!(obj.isComplexType(property) || obj.isListType(property))) {
2005                 this.copySimpleProperty(property, obj, v);
2006                 modified = true;
2007             } else {
2008                 if (obj.isComplexType(property)) {
2009                     /* container case */
2010
2011                     if (!property.equals("relationship-list") && depth >= 0) {
2012                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2013                         Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, filterCousinNodes, filterParentNodes);
2014                         if (result != null) {
2015                             obj.setValue(property, argumentObject.getUnderlyingObject());
2016                             modified = true;
2017                         }
2018                     } else if (property.equals("relationship-list") && !nodeOnly) {
2019                         /* relationships need to be handled correctly */
2020                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2021                         relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes);
2022                         if (relationshipList != null) {
2023                             modified = true;
2024                             obj.setValue(property, relationshipList.getUnderlyingObject());
2025                             modified = true;
2026                         }
2027
2028                     }
2029                 } else if (obj.isListType(property)) {
2030
2031                     if (property.equals("any")) {
2032                         continue;
2033                     }
2034                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
2035                     if (obj.isComplexGenericType(property) && depth >= 0) {
2036                         final String childDbName = convertFromCamelCase(genericType);
2037                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2038                         EdgeRule rule;
2039
2040                         boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2041
2042                         EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
2043
2044                         try {
2045                             rule = edgeRules.getRule(q);
2046                         } catch (EdgeRuleNotFoundException e) {
2047                             throw new NoEdgeRuleFoundException(e);
2048                         } catch (AmbiguousRuleChoiceException e) {
2049                             throw new MultipleEdgeRuleFoundException(e);
2050                         }
2051                         if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) {
2052                             //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
2053                             Direction ruleDirection = rule.getDirection();
2054                             Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
2055                             List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
2056                             itr = verticesList.stream().filter(item -> {
2057                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
2058                             }).iterator();
2059                             if (itr.hasNext()) {
2060                                 getList = (List<Object>) obj.getValue(property);
2061                             }
2062                             int processed = 0;
2063                             int removed = 0;
2064                             while (itr.hasNext()) {
2065                                 Vertex childVertex = itr.next();
2066                                 if (!seen.contains(childVertex)) {
2067                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2068
2069                                     Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes);
2070                                     if (result != null) {
2071                                         getList.add(argumentObject.getUnderlyingObject());
2072                                     }
2073
2074                                     processed++;
2075                                 } else {
2076                                     removed++;
2077                                     LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
2078                                 }
2079                             }
2080                             if (processed == 0) {
2081                                 //vertices were all seen, reset the list
2082                                 getList = null;
2083                             }
2084                             if (processed > 0) {
2085                                 modified = true;
2086                             }
2087                         }
2088                     } else if (obj.isSimpleGenericType(property)) {
2089                         List<Object> temp = this.engine.getListProperty(v, property);
2090                         if (temp != null) {
2091                             getList = (List<Object>) obj.getValue(property);
2092                             getList.addAll(temp);
2093                             modified = true;
2094                         }
2095
2096                     }
2097
2098                 }
2099
2100             }
2101         }
2102
2103         //no changes were made to this obj, discard the instance
2104         if (!modified) {
2105             return null;
2106         }
2107         this.enrichData(obj, v);
2108         return obj;
2109
2110     }
2111
2112     /**
2113      * Creates the relationship list with the filtered node types.
2114      *
2115      * @param v       the v
2116      * @param obj     the obj
2117      * @param cleanUp the clean up
2118      * @return the object
2119      * @throws InstantiationException       the instantiation exception
2120      * @throws IllegalAccessException       the illegal access exception
2121      * @throws IllegalArgumentException     the illegal argument exception
2122      * @throws InvocationTargetException    the invocation target exception
2123      * @throws NoSuchMethodException        the no such method exception
2124      * @throws SecurityException            the security exception
2125      * @throws UnsupportedEncodingException the unsupported encoding exception
2126      * @throws AAIException                 the AAI exception
2127      * @throws MalformedURLException        the malformed URL exception
2128      * @throws URISyntaxException
2129      */
2130     private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException {
2131         List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2132
2133         Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2134             String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2135             return filterNodes.parallelStream().anyMatch(node::contains);
2136         }).iterator();
2137
2138
2139         List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices);
2140
2141         //items.parallelStream().anyMatch(inputStr::contains)
2142         List<Object> relationshipObjList = obj.getValue("relationship");
2143         for (Vertex cousin : cousins) {
2144
2145             Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
2146             Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
2147             if (result != null) {
2148                 relationshipObjList.add(result);
2149             }
2150
2151
2152         }
2153
2154         if (relationshipObjList.isEmpty()) {
2155             return null;
2156         } else {
2157             return obj;
2158         }
2159     }
2160
2161 }