aai-common support for v20
[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-2019 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 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import com.google.common.base.CaseFormat;
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.tinkerpop.gremlin.process.traversal.Path;
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.Pair;
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.enums.AAIDirection;
42 import org.onap.aai.edges.enums.EdgeField;
43 import org.onap.aai.edges.enums.EdgeProperty;
44 import org.onap.aai.edges.enums.EdgeType;
45 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
46 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.introspection.*;
49 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
50 import org.onap.aai.introspection.sideeffect.*;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.logging.LogFormatTools;
53 import org.onap.aai.logging.StopWatch;
54 import org.onap.aai.parsers.query.QueryParser;
55 import org.onap.aai.parsers.relationship.RelationshipToURI;
56 import org.onap.aai.parsers.uri.URIParser;
57 import org.onap.aai.parsers.uri.URIToObject;
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.util.delta.*;
71 import org.onap.aai.workarounds.NamingExceptions;
72 import org.springframework.context.ApplicationContext;
73
74 import javax.ws.rs.core.UriBuilder;
75 import java.io.UnsupportedEncodingException;
76 import java.lang.reflect.Array;
77 import java.lang.reflect.InvocationTargetException;
78 import java.net.MalformedURLException;
79 import java.net.URI;
80 import java.net.URISyntaxException;
81 import java.util.*;
82 import java.util.concurrent.ExecutionException;
83 import java.util.concurrent.ExecutorService;
84 import java.util.concurrent.Future;
85 import java.util.regex.Matcher;
86 import java.util.regex.Pattern;
87
88 public class DBSerializer {
89
90     private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class);
91     private static final String RELATIONSHIP_LABEL = "relationship-label";
92     private static final String RELATIONSHIP = "relationship";
93     public static final String FALSE = "false";
94     public static final String AAI_6145 = "AAI_6145";
95     public static final String AAI_6129 = "AAI_6129";
96
97     private final TransactionalGraphEngine engine;
98     private final String sourceOfTruth;
99     private final ModelType introspectionType;
100     private final SchemaVersion version;
101     private final Loader latestLoader;
102     private EdgeSerializer edgeSer;
103     private EdgeIngestor edgeRules;
104     private final Loader loader;
105     private final String baseURL;
106     private double dbTimeMsecs = 0;
107     private long currentTimeMillis;
108
109     private SchemaVersions schemaVersions;
110     private Set<String> namedPropNodes;
111     private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
112     private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
113     private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
114     private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>();
115     private int notificationDepth;
116     private boolean isDeltaEventsEnabled;
117
118     /**
119      * Instantiates a new DB serializer.
120      *
121      * @param version the version
122      * @param engine the engine
123      * @param introspectionType the introspection type
124      * @param sourceOfTruth the source of truth
125      * @throws AAIException
126      */
127     public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
128             String sourceOfTruth) throws AAIException {
129         this.engine = engine;
130         this.sourceOfTruth = sourceOfTruth;
131         this.introspectionType = introspectionType;
132         this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
133         SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
134         this.latestLoader =
135                 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
136         this.version = version;
137         this.loader =
138                 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
139         this.namedPropNodes = this.latestLoader.getNamedPropNodes();
140         this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
141         this.currentTimeMillis = System.currentTimeMillis();
142         // If creating the DBSerializer the old way then set the notification depth to maximum
143         this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
144         initBeans();
145     }
146
147     public DBSerializer(SchemaVersion version,
148                         TransactionalGraphEngine engine,
149                         ModelType introspectionType,
150                         String sourceOfTruth,
151                         int notificationDepth) throws AAIException {
152         this.engine = engine;
153         this.sourceOfTruth = sourceOfTruth;
154         this.introspectionType = introspectionType;
155         this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
156         SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
157         this.latestLoader =
158             SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
159         this.version = version;
160         this.loader =
161             SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
162         this.namedPropNodes = this.latestLoader.getNamedPropNodes();
163         this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
164         this.currentTimeMillis = System.currentTimeMillis();
165         this.notificationDepth = notificationDepth;
166         initBeans();
167     }
168
169     public DBSerializer(SchemaVersion version,
170                         TransactionalGraphEngine engine,
171                         ModelType introspectionType,
172                         String sourceOfTruth,
173                         int notificationDepth,
174                         String serverBase) throws AAIException {
175         this.engine = engine;
176         this.sourceOfTruth = sourceOfTruth;
177         this.introspectionType = introspectionType;
178         this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
179         SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
180         this.latestLoader =
181             SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
182         this.version = version;
183         this.loader =
184             SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
185         this.namedPropNodes = this.latestLoader.getNamedPropNodes();
186         this.baseURL = serverBase;
187         this.currentTimeMillis = System.currentTimeMillis();
188         this.notificationDepth = notificationDepth;
189         initBeans();
190     }
191
192     private void initBeans() {
193         // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
194         ApplicationContext ctx = SpringContextAware.getApplicationContext();
195         EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
196         setEdgeIngestor(ei);
197         EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
198         setEdgeSerializer(es);
199         isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
200     }
201
202     public void setEdgeSerializer(EdgeSerializer edgeSer) {
203         this.edgeSer = edgeSer;
204     }
205
206     public EdgeSerializer getEdgeSeriailizer() {
207         return this.edgeSer;
208     }
209
210     public void setEdgeIngestor(EdgeIngestor ei) {
211         this.edgeRules = ei;
212     }
213
214     public EdgeIngestor getEdgeIngestor() {
215         return this.edgeRules;
216     }
217
218     public Map<Vertex, Boolean> getUpdatedVertexes() {
219         return updatedVertexes;
220     }
221
222     public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair(){
223         return impliedDeleteUriObjectPair;
224     }
225
226     /**
227      * Touch standard vertex properties.
228      *  @param v the v
229      * @param isNewVertex the is new vertex
230      */
231     public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
232         String timeNowInSec = Long.toString(currentTimeMillis);
233         if (isNewVertex) {
234             String uuid = UUID.randomUUID().toString();
235             v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
236             v.property(AAIProperties.CREATED_TS, currentTimeMillis);
237             v.property(AAIProperties.AAI_UUID, uuid);
238             v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
239             v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
240             v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
241         } else {
242             if(isDeltaEventsEnabled) {
243                 standardVertexPropsDeltas(v, timeNowInSec);
244             }
245             v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
246             v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
247             v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
248         }
249     }
250
251     private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
252         String uri = v.property(AAIProperties.AAI_URI).value().toString();
253         long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
254         DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
255         if (getObjectDeltas().containsKey(uri)) {
256             getObjectDeltas().get(uri).setAction(objDeltaAction);
257         }
258
259         addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()), objDeltaAction);
260         addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()), objDeltaAction);
261         addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()), objDeltaAction);
262         addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()), objDeltaAction);
263
264         if (objDeltaAction.equals(DeltaAction.UPDATE)) {
265             addPropDelta(
266                 uri,
267                 AAIProperties.RESOURCE_VERSION,
268                 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
269                 objDeltaAction
270             );
271             addPropDelta(
272                 uri,
273                 AAIProperties.LAST_MOD_TS,
274                 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
275                 objDeltaAction
276             );
277             addPropDelta(
278                 uri,
279                 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
280                 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
281                 objDeltaAction
282             );
283         } else {
284             addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
285             addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
286             addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
287         }
288     }
289
290     public Map<String, ObjectDelta> getObjectDeltas() {return objectDeltas;}
291
292     private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
293         ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
294         objectDelta.addPropertyDelta(prop, delta);
295         objectDeltas.put(uri, objectDelta);
296     }
297
298     private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
299         ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
300         objectDelta.addRelationshipDelta(delta);
301         objectDeltas.put(uri, objectDelta);
302     }
303
304     private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
305         v.property(AAIProperties.NODE_TYPE, nodeType);
306         touchStandardVertexProperties(v, isNewVertex);
307     }
308
309     /**
310      * Creates the new vertex.
311      *
312      * @param wrappedObject the wrapped object
313      * @return the vertex
314      */
315     public Vertex createNewVertex(Introspector wrappedObject) {
316         Vertex v;
317         try {
318             StopWatch.conditionalStart();
319             v = this.engine.tx().addVertex(wrappedObject.getDbName());
320             touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
321         } finally {
322             dbTimeMsecs += StopWatch.stopIfStarted();
323         }
324         return v;
325     }
326
327     /**
328      * Trim class name.
329      *
330      * @param className the class name
331      * @return the string
332      */
333     /*
334      * Removes the classpath from a class name
335      */
336     public String trimClassName(String className) {
337         String returnValue = "";
338
339         if (className.lastIndexOf('.') == -1) {
340             return className;
341         }
342         returnValue = className.substring(className.lastIndexOf('.') + 1);
343
344         return returnValue;
345     }
346
347     /**
348      * Serialize to db.
349      *
350      * @param obj the obj
351      * @param v the v
352      * @param uriQuery the uri query
353      * @param identifier the identifier
354      * @throws SecurityException the security exception
355      * @throws IllegalArgumentException the illegal argument exception
356      * @throws AAIException the AAI exception
357      * @throws UnsupportedEncodingException the unsupported encoding exception
358      */
359     public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
360             String requestContext) throws AAIException, UnsupportedEncodingException {
361         StopWatch.conditionalStart();
362         try {
363             if (uriQuery.isDependent()) {
364                 // try to find the parent
365                 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
366                 if (!vertices.isEmpty()) {
367                     Vertex parent = vertices.get(0);
368                     this.reflectDependentVertex(parent, v, obj, requestContext);
369                 } else {
370                     dbTimeMsecs += StopWatch.stopIfStarted();
371                     throw new AAIException("AAI_6114",
372                             "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
373                 }
374             } else {
375                 serializeSingleVertex(v, obj, requestContext);
376             }
377
378         } catch (SchemaViolationException e) {
379             dbTimeMsecs += StopWatch.stopIfStarted();
380             throw new AAIException("AAI_6117", e);
381         }
382         dbTimeMsecs += StopWatch.stopIfStarted();
383     }
384
385     public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
386             throws UnsupportedEncodingException, AAIException {
387         StopWatch.conditionalStart();
388         try {
389             boolean isTopLevel = obj.isTopLevel();
390             if (isTopLevel) {
391                 addUriIfNeeded(v, obj.getURI());
392             }
393             if (!isTopLevel) {
394                 URI uri = this.getURIForVertex(v);
395                 URIParser parser = new URIParser(this.loader, uri);
396                 if (parser.validate()) {
397                     addUriIfNeeded(v, uri.toString());
398                 }
399             }
400             processObject(obj, v, requestContext);
401         } catch (SchemaViolationException e) {
402             throw new AAIException("AAI_6117", e);
403         } finally {
404             dbTimeMsecs += StopWatch.stopIfStarted();
405         }
406     }
407
408     private void addUriIfNeeded(Vertex v, String uri) {
409         VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
410         if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
411             v.property(AAIProperties.AAI_URI, uri);
412         }
413     }
414
415     /**
416      * Process object.
417      *
418      * @param obj the obj
419      * @param v the v
420      * @return the list
421      * @throws IllegalArgumentException the illegal argument exception
422      * @throws SecurityException the security exception
423      * @throws AAIException the AAI exception
424      * @throws UnsupportedEncodingException the unsupported encoding exception
425      */
426     /*
427      * Helper method for reflectToDb
428      * Handles all the property setting
429      */
430     private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
431             throws UnsupportedEncodingException, AAIException {
432         Set<String> properties = new LinkedHashSet<>(obj.getProperties());
433         properties.remove(AAIProperties.RESOURCE_VERSION);
434         List<Vertex> dependentVertexes = new ArrayList<>();
435         List<Vertex> processedVertexes = new ArrayList<>();
436
437         boolean isComplexType ;
438         boolean isListType;
439
440         // If the notification depth is set to maximum
441         // this is the behavior of the expected clients
442         if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
443             if (!obj.isContainer()) {
444                 this.touchStandardVertexProperties(v, false);
445             }
446         }
447         this.executePreSideEffects(obj, v);
448         for (String property : properties) {
449             final String propertyType;
450             propertyType = obj.getType(property);
451             isComplexType = obj.isComplexType(property);
452             isListType = obj.isListType(property);
453             Object value = obj.getValue(property);
454
455             if (!(isComplexType || isListType)) {
456                 boolean canModify = this.canModify(obj, property, requestContext);
457
458                 if (canModify) {
459                     final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
460                     String dbProperty = property;
461                     if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
462                         dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
463                     }
464                     if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
465                         // data linked properties are ephemeral
466                         // they are populated dynamically on GETs
467                         continue;
468                     }
469                     Object oldValue = v.property(dbProperty).orElse(null);
470                     String uri = getURIForVertex(v).toString();
471                     if (value != null) {
472                         if (!value.equals(oldValue)) {
473                             if (propertyType.toLowerCase().contains(".long")) {
474                                 v.property(dbProperty, new Integer(((Long) value).toString()));
475                             } else {
476                                 v.property(dbProperty, value);
477                             }
478                             if (isDeltaEventsEnabled) {
479                                 createDeltaProperty(uri, value, dbProperty, oldValue);
480                             }
481                             this.updatedVertexes.putIfAbsent(v, false);
482                         }
483                     } else {
484                         if (oldValue != null) {
485                             v.property(dbProperty).remove();
486                             if (isDeltaEventsEnabled) {
487                                 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
488                             }
489                             this.updatedVertexes.putIfAbsent(v, false);
490                         }
491                     }
492                 }
493             } else if (isListType) {
494                 List<Object> list = (List<Object>) value;
495                 if (obj.isComplexGenericType(property)) {
496                     if (list != null) {
497                         for (Object o : list) {
498                             Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
499                             child.setURIChain(obj.getURI());
500                             processedVertexes.add(reflectDependentVertex(v, child, requestContext));
501                         }
502                     }
503                 } else {
504                     // simple list case
505                     if (isDeltaEventsEnabled) {
506                         String uri = getURIForVertex(v).toString();
507                         List<Object> oldVal = engine.getListProperty(v, property);
508                         engine.setListProperty(v, property, list);
509                         if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
510                             if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
511                                 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
512                             }
513                         } else { // is either a create or update and is handled by the called method
514                             createDeltaProperty(uri, list, property, oldVal);
515                         }
516                     } else {
517                         engine.setListProperty(v, property, list);
518                     }
519                     this.updatedVertexes.putIfAbsent(v, false);
520                 }
521             } else {
522                 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
523                 // back to this method
524                 if (value != null) { // effectively ignore complex properties not included in the object we're
525                                      // processing
526                     if (value.getClass().isArray()) {
527
528                         int length = Array.getLength(value);
529                         for (int i = 0; i < length; i++) {
530                             Object arrayElement = Array.get(value, i);
531                             Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
532                             child.setURIChain(obj.getURI());
533                             processedVertexes.add(reflectDependentVertex(v, child, requestContext));
534
535                         }
536                     } else if (!property.equals("relationship-list")) {
537                         // container case
538                         Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
539                         if (introspector.isContainer()) {
540                             dependentVertexes.addAll(
541                                     this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
542                             introspector.setURIChain(obj.getURI());
543
544                             processedVertexes.addAll(processObject(introspector, v, requestContext));
545
546                         } else {
547                             dependentVertexes.addAll(
548                                     this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
549                             processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
550
551                         }
552                     } else if (property.equals("relationship-list")) {
553                         handleRelationships(obj, v);
554                     }
555                 }
556             }
557         }
558         this.writeThroughDefaults(v, obj);
559         /* handle those vertexes not touched */
560         for (Vertex toBeKept : processedVertexes) {
561             dependentVertexes.remove(toBeKept);
562         }
563
564         ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
565         List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
566
567         if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
568             for(Vertex curVertex : impliedDeleteVertices){
569                 if(!curVertex.property("aai-uri").isPresent()){
570                     LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
571                     continue;
572                 }
573                 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
574                 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
575
576                 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
577
578                 if(!curObj.isTopLevel()){
579                     curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
580                 }
581
582                 if(!impliedDeleteUriObjectPair.containsKey(curAaiUri)){
583                     impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
584                 }
585             }
586         }
587
588         impliedDelete.delete(impliedDeleteVertices);
589
590         // touch svp using vertex list for what changed
591         // if the notification depth is zero
592         if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
593             this.updatedVertexes.entrySet().stream()
594                 .filter(e -> !e.getValue())
595                 .filter(e -> !edgeVertexes.contains(e.getKey()))
596                 .forEach(e -> {
597                     this.touchStandardVertexProperties(e.getKey(), false);
598                     e.setValue(true);
599                 });
600         }
601         this.executePostSideEffects(obj, v);
602         return processedVertexes;
603     }
604
605     private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
606         if (oldValue == null) {
607             addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
608         } else {
609             addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
610         }
611     }
612
613     public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
614                                                             Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
615
616         HashMap<String, Introspector> relatedVertices = new HashMap<>();
617         VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
618
619         if (!aaiUriProperty.isPresent()) {
620             if (LOGGER.isDebugEnabled()) {
621                 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
622                     v.id().toString());
623             } else {
624                 LOGGER.info(
625                     "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
626             }
627             return relatedVertices;
628         }
629
630         String aaiUri = aaiUriProperty.value().toString();
631
632         if (!obj.isTopLevel()) {
633             String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
634             List<Vertex> vertexChain;
635             // If the uriList is null then there is something wrong with converting the uri
636             // into a list of aai-uris so falling back to the old mechanism for finding parents
637             if (uriList == null) {
638                 LOGGER.info(
639                     "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
640                 vertexChain = queryEngine.findParents(v);
641             } else if (uriList.length == 1) {
642                 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
643                 vertexChain = Collections.singletonList(v);
644             } else {
645                 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
646                 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
647                 // inject v into start of vertexChain
648                 vertexChain.add(0, v);
649             }
650             for (Vertex vertex : vertexChain) {
651                 try {
652                     final Introspector vertexObj = this.getVertexProperties(vertex);
653                     relatedVertices.put(vertexObj.getObjectId(), vertexObj);
654                 } catch (AAIUnknownObjectException e) {
655                     LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
656                 }
657             }
658         } else {
659             try {
660                 final Introspector vertexObj = this.getVertexProperties(v);
661                 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
662             } catch (AAIUnknownObjectException e) {
663                 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
664             }
665         }
666
667         return relatedVertices;
668     }
669
670     /**
671      * Given an uri, introspector object and loader object
672      * it will check if the obj is top level object if it is,
673      * it will return immediately returning the uri passed in
674      * If it isn't, it will go through, get the uriTemplate
675      * from the introspector object and get the count of "/"s
676      * and remove that part of the uri using substring
677      * and keep doing that until the current object is top level
678      * Also added the max depth just so worst case scenario
679      * Then keep adding aai-uri to the list include the aai-uri passed in
680      * Convert that list into an array and return it
681      * <p>
682      *
683      * Example:
684      *
685      * <blockquote>
686      * aai-uri ->
687      * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
688      *
689      * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
690      * it converts to /vservers/vserver
691      *
692      * lastIndexOf /vservers/vserver in
693      * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
694      * ^
695      * |
696      * |
697      * lastIndexOf
698      * Use substring to get the string from 0 to that lastIndexOf
699      * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
700      *
701      * From this new aai-uri, generate a introspector from the URITOObject class
702      * and keep doing this until you
703      *
704      * </blockquote>
705      *
706      * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
707      * @param obj - introspector object of the given starting vertex
708      * @param loader - Type of loader which will always be MoxyLoader to support model driven
709      * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
710      * @throws UnsupportedEncodingException
711      * @throws AAIException
712      */
713     String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
714         throws UnsupportedEncodingException, AAIException {
715
716         List<String> uriList = new ArrayList<>();
717         String template;
718         String truncatedUri = aaiUri;
719         int depth = AAIProperties.MAXIMUM_DEPTH;
720         uriList.add(truncatedUri);
721
722         while (depth >= 0 && !obj.isTopLevel()) {
723             template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
724
725             if (template == null) {
726                 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
727                 return null;
728             }
729
730             int templateCount = StringUtils.countMatches(template, "/");
731             int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
732
733             if (templateCount > truncatedUriCount) {
734                 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
735                 return null;
736             }
737
738             int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
739             truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
740             uriList.add(truncatedUri);
741             obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
742             depth--;
743         }
744
745         return uriList.toArray(new String[0]);
746     }
747
748     /**
749      * Handle relationships.
750      *
751      * @param obj the obj
752      * @param vertex the vertex
753      * @throws SecurityException the security exception
754      * @throws IllegalArgumentException the illegal argument exception
755      * @throws UnsupportedEncodingException the unsupported encoding exception
756      * @throws AAIException the AAI exception
757      */
758     /*
759      * Handles the explicit relationships defined for an obj
760      */
761     private void handleRelationships(Introspector obj, Vertex vertex)
762             throws UnsupportedEncodingException, AAIException {
763
764         Introspector wrappedRl = obj.getWrappedValue("relationship-list");
765         processRelationshipList(wrappedRl, vertex);
766
767     }
768
769     /**
770      * Process relationship list.
771      *
772      * @param wrapped the wrapped
773      * @param v the v
774      * @throws UnsupportedEncodingException the unsupported encoding exception
775      * @throws AAIException the AAI exception
776      */
777     private void processRelationshipList(Introspector wrapped, Vertex v)
778         throws UnsupportedEncodingException, AAIException {
779
780         List<Object> relationships = wrapped.getValue("relationship");
781         String mainUri = getURIForVertex(v).toString();
782         String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
783         EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
784             .edgeType(EdgeType.COUSIN)
785             .version(wrapped.getVersion());
786         EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
787             .edgeType(EdgeType.TREE)
788             .version(wrapped.getVersion());
789
790         EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
791
792         Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
793         for (Object relationship : relationships) {
794
795             String label;
796             Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
797             String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
798
799             if (relUri.startsWith("/vnf/")) {
800                 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
801                 List<Vertex> results = parser.getQueryBuilder().toList();
802                 if (results.isEmpty()) {
803                     final AAIException ex = new AAIException(AAI_6129,
804                         String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
805                     ex.getTemplateVars().add(parser.getResultType());
806                     ex.getTemplateVars().add(parser.getUri().toString());
807                     throw ex;
808                 } else {
809                     // still an issue if there's more than one
810                     if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
811                         relUri = results.get(0).value(AAIProperties.AAI_URI);
812                     } else {
813                         LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
814                         continue;
815                     }
816                 }
817             }
818
819             if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
820                 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
821             } else {
822                 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
823                 String bNodeType = uriToObject.getEntityName();
824                 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
825                 if (!edgeIngestor.hasRule(ruleQuery)) {
826                     EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
827                     if (edgeIngestor.hasRule(treeQuery)) {
828                         throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
829                     }
830                     throw new AAIException("AAI_6120", String.format(
831                         "No EdgeRule found for passed nodeTypes: %s, %s.",
832                         aNodeType, bNodeType));
833                 } else {
834                     try {
835                         final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
836                         if (rules.size() == 1) {
837                             label = rules.get(0).getLabel();
838                         } else {
839                             Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
840                             if (defaultRule.isPresent()) {
841                                 label = defaultRule.get().getLabel();
842                             } else {
843                                 throw new AAIException(AAI_6145);
844                             }
845                         }
846                     } catch (EdgeRuleNotFoundException ea) {
847                         throw new AAIException(AAI_6145, ea);
848                     }
849                 }
850             }
851             cousinUriAndLabels.add(Pair.with(relUri, label));
852         }
853
854         List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
855         Set<Path> toRemove = new HashSet<>();
856
857
858         //  for each path 3 things can happen:
859         //      1. The edge rule that created it is not in this version no action is to be taken on that edge
860         //      2. The edge rule exits in this version it's included in the request the edge is left alone
861         //      3. The edge rule exits in this version and is not included in the request it is marked for removal
862         for (Path path : paths) {
863             if (path.size() < 3) {
864                 continue;
865             }
866
867             // Path represents
868             //       v   ----related-to-->    otherV
869             // In the above case,
870             // path objects get(0) returns vertex v
871             // path objects.get(1) returns edge related-to
872             // path objects.get(2) returns vertex otherV
873             Vertex otherV = path.get(2);
874
875             String bUri;
876             if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
877                 bUri = otherV.value(AAIProperties.AAI_URI);
878             } else {
879                 continue;
880             }
881             String edgeLabel = path.<Edge>get(1).label();
882
883             Pair<String, String> key = Pair.with(bUri, edgeLabel);
884             if (cousinUriAndLabels.contains(key)) {
885                 cousinUriAndLabels.remove(key);
886             } else {
887                 String bNodeType;
888                 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
889                     bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
890                 } else {
891                     continue;
892                 }
893                 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
894                 if (edgeIngestor.hasRule(ruleQuery)) {
895                     toRemove.add(path);
896                 }
897             }
898
899         }
900
901         Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
902         for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
903             Edge e;
904             Vertex cousinVertex;
905             String label = cousinUriAndLabel.getValue1();
906             String cousinUri = cousinUriAndLabel.getValue0();
907             QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
908
909             List<Vertex> results = parser.getQueryBuilder().toList();
910             if (results.isEmpty()) {
911                 final AAIException ex = new AAIException(AAI_6129,
912                     "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
913                 ex.getTemplateVars().add(parser.getResultType());
914                 ex.getTemplateVars().add(parser.getUri().toString());
915                 throw ex;
916             } else {
917                 // still an issue if there's more than one
918                 cousinVertex = results.get(0);
919             }
920
921             if (cousinVertex != null) {
922                 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
923                 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
924                 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
925
926                 if (!edgeRules.hasRule(baseQ.build())) {
927                     throw new AAIException("AAI_6120", String.format(
928                         "No EdgeRule found for passed nodeTypes: %s, %s%s.",
929                         aNodeType, cousinType, label != null ? (" with label " + label) : ""));
930                 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
931                     && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
932                     throw new AAIException(AAI_6145);
933                 }
934
935                 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
936
937                 if (e == null) {
938                     toBeCreated.add(Pair.with(cousinVertex, label));
939                 }
940             }
941         }
942
943         for (Path path : toRemove) {
944             if(isDeltaEventsEnabled) {
945                 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
946             }
947             this.updatedVertexes.putIfAbsent(v, false);
948             this.edgeVertexes.add(path.get(2));
949             path.<Edge>get(1).remove();
950         }
951
952         for (Pair<Vertex, String> create : toBeCreated) {
953             try {
954                 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
955                 if (isDeltaEventsEnabled) {
956                     deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
957                 }
958                 this.updatedVertexes.putIfAbsent(v, false);
959                 this.edgeVertexes.add(create.getValue0());
960             } catch (NoEdgeRuleFoundException ex) {
961                 throw new AAIException(AAI_6129, ex);
962             }
963         }
964     }
965
966
967     private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
968         RelationshipDelta relationshipDelta = new RelationshipDelta(
969             edgeAction,
970             edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
971             edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
972             edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
973             edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
974             edge.label());
975         edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
976         addRelationshipDelta(mainUri, relationshipDelta, mainAction);
977     }
978
979     /**
980      * Write through defaults.
981      *
982      * @param v the v
983      * @param obj the obj
984      * @throws AAIUnknownObjectException
985      */
986     private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
987         Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
988         if (latest != null) {
989             Set<String> required = latest.getRequiredProperties();
990
991             for (String field : required) {
992                 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
993                 if (defaultValue != null) {
994                     Object vertexProp = v.property(field).orElse(null);
995                     if (vertexProp == null) {
996                         v.property(field, defaultValue);
997                     }
998                 }
999             }
1000         }
1001
1002     }
1003
1004     /**
1005      * Reflect dependent vertex.
1006      *
1007      * @param v the v
1008      * @param dependentObj the dependent obj
1009      * @return the vertex
1010      * @throws IllegalArgumentException the illegal argument exception
1011      * @throws SecurityException the security exception
1012      * @throws AAIException the AAI exception
1013      * @throws UnsupportedEncodingException the unsupported encoding exception
1014      * @throws AAIUnknownObjectException
1015      */
1016     private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1017             throws AAIException, UnsupportedEncodingException {
1018
1019         QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1020         query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1021         query.createKeyQuery(dependentObj);
1022
1023         List<Vertex> items = query.toList();
1024
1025         Vertex dependentVertex;
1026         if (items.size() == 1) {
1027             dependentVertex = items.get(0);
1028             this.verifyResourceVersion("update", dependentObj.getDbName(),
1029                     dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1030                     dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1031         } else {
1032             this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1033                     dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1034             dependentVertex = createNewVertex(dependentObj);
1035         }
1036
1037         return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1038
1039     }
1040
1041     /**
1042      * Reflect dependent vertex.
1043      *
1044      * @param parent the parent
1045      * @param child the child
1046      * @param obj the obj
1047      * @return the vertex
1048      * @throws IllegalArgumentException the illegal argument exception
1049      * @throws SecurityException the security exception
1050      * @throws AAIException the AAI exception
1051      * @throws UnsupportedEncodingException the unsupported encoding exception
1052      * @throws AAIUnknownObjectException
1053      */
1054     private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1055             throws AAIException, UnsupportedEncodingException {
1056
1057         String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1058         if (parentUri != null) {
1059             String uri;
1060             uri = obj.getURI();
1061             addUriIfNeeded(child, parentUri + uri);
1062         }
1063         processObject(obj, child, requestContext);
1064
1065         Edge e;
1066         e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1067
1068         if (e == null) {
1069             String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1070             if (canBeLinked != null && canBeLinked.equals("true")) {
1071                 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1072                         .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1073                 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1074                         .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1075                 if (isFirst) {
1076                     child.property(AAIProperties.LINKED, true);
1077                 }
1078             }
1079             e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1080             if(isDeltaEventsEnabled) {
1081                 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1082             }
1083         }
1084         return child;
1085
1086     }
1087
1088     private SchemaVersion getVerForContext(String requestContext) {
1089         Pattern pattern = Pattern.compile("v[0-9]+");
1090         Matcher m = pattern.matcher(requestContext);
1091         if (!m.find()) {
1092             return this.version;
1093         } else {
1094             return new SchemaVersion(requestContext);
1095         }
1096     }
1097
1098     /**
1099      * Db to object.
1100      *
1101      * @param vertices the vertices
1102      * @param obj the obj
1103      * @param depth the depth
1104      * @param cleanUp the clean up
1105      * @return the introspector
1106      * @throws AAIException the AAI exception
1107      * @throws IllegalArgumentException the illegal argument exception
1108      * @throws SecurityException the security exception
1109      * @throws UnsupportedEncodingException the unsupported encoding exception
1110      * @throws AAIUnknownObjectException
1111      * @throws URISyntaxException
1112      */
1113     public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1114                                    String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1115         final int internalDepth;
1116         if (depth == Integer.MAX_VALUE) {
1117             internalDepth = depth--;
1118         } else {
1119             internalDepth = depth;
1120         }
1121         StopWatch.conditionalStart();
1122         if (vertices.size() > 1 && !obj.isContainer()) {
1123             dbTimeMsecs += StopWatch.stopIfStarted();
1124             throw new AAIException("AAI_6136",
1125                 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1126         } else if (obj.isContainer()) {
1127             final List getList;
1128             String listProperty = null;
1129             for (String property : obj.getProperties()) {
1130                 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1131                     listProperty = property;
1132                     break;
1133                 }
1134             }
1135             final String propertyName = listProperty;
1136             getList = obj.getValue(listProperty);
1137
1138             /*
1139              * This is an experimental multithreading experiment
1140              * on get alls.
1141              */
1142             ExecutorService pool = GetAllPool.getInstance().getPool();
1143
1144             List<Future<Object>> futures = new ArrayList<>();
1145
1146             for (Vertex v : vertices) {
1147                 AaiCallable<Object> task = new AaiCallable<Object>() {
1148                     @Override
1149                     public Object process() throws UnsupportedEncodingException, AAIException {
1150                         Set<Vertex> seen = new HashSet<>();
1151                         Introspector childObject;
1152                         childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1153                         dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1154                         return childObject.getUnderlyingObject();
1155                     }
1156                 };
1157                 futures.add(pool.submit(task));
1158             }
1159
1160             for (Future<Object> future : futures) {
1161                 try {
1162                     getList.add(future.get());
1163                 } catch (ExecutionException | InterruptedException e) {
1164                     dbTimeMsecs += StopWatch.stopIfStarted();
1165                     throw new AAIException("AAI_4000", e);
1166                 }
1167             }
1168         } else if (vertices.size() == 1) {
1169             Set<Vertex> seen = new HashSet<>();
1170             dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1171         } else {
1172             // obj = null;
1173         }
1174
1175         dbTimeMsecs += StopWatch.stopIfStarted();
1176         return obj;
1177     }
1178
1179     /**
1180      * Db to object.
1181      *
1182      * @param vertices the vertices
1183      * @param obj the obj
1184      * @param depth the depth
1185      * @param cleanUp the clean up
1186      * @return the introspector
1187      * @throws AAIException the AAI exception
1188      * @throws IllegalArgumentException the illegal argument exception
1189      * @throws SecurityException the security exception
1190      * @throws UnsupportedEncodingException the unsupported encoding exception
1191      * @throws AAIUnknownObjectException
1192      * @throws URISyntaxException
1193      */
1194     public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1195             String cleanUp) throws UnsupportedEncodingException, AAIException {
1196         return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1197     }
1198
1199     /**
1200      * Db to object.
1201      *
1202      * @param obj the obj
1203      * @param v the v
1204      * @param seen the seen
1205      * @param depth the depth
1206      * @param cleanUp the clean up
1207      * @return the introspector
1208      * @throws IllegalArgumentException the illegal argument exception
1209      * @throws SecurityException the security exception
1210      * @throws UnsupportedEncodingException the unsupported encoding exception
1211      * @throws AAIException the AAI exception
1212      * @throws AAIUnknownObjectException
1213      * @throws URISyntaxException
1214      */
1215     private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1216             String cleanUp) throws AAIException, UnsupportedEncodingException {
1217         return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1218     }
1219
1220     /**
1221      * Db to object.
1222      *
1223      * @param obj the obj
1224      * @param v the v
1225      * @param seen the seen
1226      * @param depth the depth
1227      * @param cleanUp the clean up
1228      * @return the introspector
1229      * @throws IllegalArgumentException the illegal argument exception
1230      * @throws SecurityException the security exception
1231      * @throws UnsupportedEncodingException the unsupported encoding exception
1232      * @throws AAIException the AAI exception
1233      * @throws AAIUnknownObjectException
1234      * @throws URISyntaxException
1235      */
1236     private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1237                                     String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1238
1239         if (depth < 0) {
1240             return null;
1241         }
1242         depth--;
1243         seen.add(v);
1244
1245         boolean modified = false;
1246         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1247             List<Object> getList = null;
1248
1249             if (!(obj.isComplexType(property) || obj.isListType(property))) {
1250                 this.copySimpleProperty(property, obj, v);
1251                 modified = true;
1252             } else {
1253                 if (obj.isComplexType(property)) {
1254                     /* container case */
1255
1256                     if (!property.equals("relationship-list") && depth >= 0) {
1257                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1258                         Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1259                         if (result != null) {
1260                             obj.setValue(property, argumentObject.getUnderlyingObject());
1261                             modified = true;
1262                         }
1263                     } else if (property.equals("relationship-list") && !nodeOnly) {
1264                         /* relationships need to be handled correctly */
1265                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1266                         relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1267                         if (relationshipList != null) {
1268                             obj.setValue(property, relationshipList.getUnderlyingObject());
1269                             modified = true;
1270                         }
1271
1272                     }
1273                 } else if (obj.isListType(property)) {
1274
1275                     if (property.equals("any")) {
1276                         continue;
1277                     }
1278                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
1279                     if (obj.isComplexGenericType(property) && depth >= 0) {
1280                         final String childDbName = convertFromCamelCase(genericType);
1281                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1282                         EdgeRule rule;
1283
1284                         try {
1285                             rule = edgeRules.getRule(
1286                                 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1287                         } catch (EdgeRuleNotFoundException e) {
1288                             throw new NoEdgeRuleFoundException(e);
1289                         } catch (AmbiguousRuleChoiceException e) {
1290                             throw new MultipleEdgeRuleFoundException(e);
1291                         }
1292                         if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1293
1294                             Direction ruleDirection = rule.getDirection();
1295                             List<Vertex> verticesList = new ArrayList<>();
1296                             v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1297                                 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1298                                     verticesList.add(vertex);
1299                                 }
1300                             });
1301                             if (!verticesList.isEmpty()) {
1302                                 getList = obj.getValue(property);
1303                             }
1304                             int processed = 0;
1305                             for (Vertex childVertex : verticesList) {
1306                                 if (!seen.contains(childVertex)) {
1307                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1308
1309                                     Object result =
1310                                         dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1311                                     if (result != null) {
1312                                         getList.add(argumentObject.getUnderlyingObject());
1313                                     }
1314
1315                                     processed++;
1316                                 } else {
1317                                     LOGGER.warn("Cycle found while serializing vertex id={}",
1318                                         childVertex.id().toString());
1319                                 }
1320                             }
1321                             if (processed == 0) {
1322                                 // vertices were all seen, reset the list
1323                                 getList = null;
1324                             }
1325                             if (processed > 0) {
1326                                 modified = true;
1327                             }
1328                         }
1329                     } else if (obj.isSimpleGenericType(property)) {
1330                         List<Object> temp = this.engine.getListProperty(v, property);
1331                         if (temp != null) {
1332                             getList = (List<Object>) obj.getValue(property);
1333                             getList.addAll(temp);
1334                             modified = true;
1335                         }
1336                     }
1337                 }
1338             }
1339         }
1340
1341         // no changes were made to this obj, discard the instance
1342         if (!modified) {
1343             return null;
1344         }
1345         this.enrichData(obj, v);
1346         return obj;
1347
1348     }
1349
1350     public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1351         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1352         if (nodeType == null) {
1353             throw new AAIException("AAI_6143");
1354         }
1355
1356         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1357         Set<Vertex> seen = new HashSet<>();
1358         int depth = 0;
1359         StopWatch.conditionalStart();
1360         this.dbToObject(obj, v, seen, depth, true, FALSE);
1361         dbTimeMsecs += StopWatch.stopIfStarted();
1362         return obj;
1363
1364     }
1365
1366     public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1367         return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1368     }
1369
1370     public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1371         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1372         if (nodeType == null) {
1373             throw new AAIException("AAI_6143");
1374         }
1375         Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1376         Set<Vertex> seen = new HashSet<>();
1377         StopWatch.conditionalStart();
1378         this.dbToObject(obj, v, seen, depth, false, FALSE);
1379         dbTimeMsecs += StopWatch.stopIfStarted();
1380         return obj;
1381     }
1382
1383     /**
1384      * Copy simple property.
1385      *
1386      * @param property the property
1387      * @param obj the obj
1388      * @param v the v
1389      * @throws IllegalArgumentException the illegal argument exception
1390      * @throws SecurityException the security exception
1391      */
1392     private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1393         final Object temp = getProperty(obj, property, v);
1394         if (temp != null) {
1395             obj.setValue(property, temp);
1396         }
1397     }
1398
1399     public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1400
1401         Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1402         String[] simplePropsArray = new String[simpleProperties.size()];
1403         simplePropsArray = simpleProperties.toArray(simplePropsArray);
1404
1405         Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1406
1407         v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1408
1409         return simplePropsHashMap;
1410     }
1411
1412     public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1413         Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1414         relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1415         return relationshipList;
1416     }
1417
1418     /**
1419      * Creates the relationship list.
1420      *
1421      * @param v the v
1422      * @param obj the obj
1423      * @param cleanUp the clean up
1424      * @return the object
1425      * @throws IllegalArgumentException the illegal argument exception
1426      * @throws SecurityException the security exception
1427      * @throws UnsupportedEncodingException the unsupported encoding exception
1428      * @throws AAIException the AAI exception
1429      * @throws URISyntaxException
1430      */
1431     private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1432             throws UnsupportedEncodingException, AAIException {
1433         // default boolean value for isSkipRelatedTo is false
1434         return createRelationshipList(v, obj, cleanUp, false);
1435     }
1436
1437     /**
1438      * Creates the relationship list.
1439      *
1440      * @param v the v
1441      * @param obj the obj
1442      * @param cleanUp the clean up
1443      * @param isSkipRelatedTo to determine adding related-to-property in response
1444      * @return the object
1445      * @throws IllegalArgumentException the illegal argument exception
1446      * @throws SecurityException the security exception
1447      * @throws UnsupportedEncodingException the unsupported encoding exception
1448      * @throws AAIException the AAI exception
1449      * @throws URISyntaxException
1450      */
1451     private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1452             throws UnsupportedEncodingException, AAIException {
1453
1454         List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1455         VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1456
1457         if (!nodeTypeProperty.isPresent()) {
1458             LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1459             return null;
1460         }
1461
1462         List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1463
1464         String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1465
1466         EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1467
1468         EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1469             .edgeType(EdgeType.COUSIN)
1470             .version(obj.getVersion());
1471
1472         for (Path path : paths){
1473             if(path.size() < 3){
1474                 continue;
1475             }
1476
1477             // Path represents
1478             //       v   ----related-to-->    otherV
1479             // In the above case,
1480             // path objects get(0) returns vertex v
1481             // path objects.get(1) returns edge related-to
1482             // path objects.get(2) returns vertex otherV
1483             Edge edge = path.get(1);
1484             Vertex otherV= path.get(2);
1485
1486             // TODO: Come back and revisit this code
1487             // Create a query based on the a nodetype and b nodetype
1488             // which is also a cousin edge and ensure the version
1489             // is used properly so for example in order to be backwards
1490             // compatible if we had allowed a edge between a and b
1491             // in a previous release and we decided to remove it from
1492             // the edge rules in the future we can display the edge
1493             // only for the older apis and the new apis if the edge rule
1494             // is removed will not be seen in the newer version of the API
1495
1496             String bNodeType;
1497             if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1498                 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1499             } else {
1500                 continue;
1501             }
1502
1503             String edgeLabel = edge.label();
1504             EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1505
1506             if(!edgeIngestor.hasRule(ruleQuery)){
1507                 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1508                 continue;
1509             }
1510
1511             Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1512             Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1513             if (result != null) {
1514                 relationshipObjList.add(result);
1515             }
1516
1517         }
1518
1519         if (relationshipObjList.isEmpty()) {
1520             return null;
1521         } else {
1522             return obj;
1523         }
1524     }
1525
1526     /**
1527      * Process edge relationship.
1528      *
1529      * @param relationshipObj the relationship obj
1530      * @param edgeLabel the edge's label
1531      * @param cleanUp the clean up
1532      * @return the object
1533      * @throws IllegalArgumentException the illegal argument exception
1534      * @throws SecurityException the security exception
1535      * @throws UnsupportedEncodingException the unsupported encoding exception
1536      * @throws AAIUnknownObjectException
1537      * @throws URISyntaxException
1538      */
1539     private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1540             String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1541
1542         VertexProperty aaiUriProperty = cousin.property("aai-uri");
1543
1544         if (!aaiUriProperty.isPresent()) {
1545             return null;
1546         }
1547
1548         URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1549
1550         URIToRelationshipObject uriParser;
1551         Introspector result;
1552         try {
1553             uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1554             result = uriParser.getResult();
1555         } catch (AAIException | URISyntaxException e) {
1556             LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1557                     + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1558             return null;
1559         }
1560
1561         VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1562
1563         if (cousinVertexNodeType.isPresent()) {
1564             String cousinType = cousinVertexNodeType.value().toString();
1565             if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1566                 this.addRelatedToProperty(result, cousin, cousinType);
1567             }
1568         }
1569
1570         if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1571             result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1572         }
1573
1574         return result.getUnderlyingObject();
1575     }
1576
1577     /**
1578      * Gets the URI for vertex.
1579      *
1580      * @param v the v
1581      * @return the URI for vertex
1582      * @throws IllegalArgumentException the illegal argument exception
1583      * @throws SecurityException the security exception
1584      * @throws UnsupportedEncodingException the unsupported encoding exception
1585      * @throws AAIUnknownObjectException
1586      */
1587     public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1588
1589         return getURIForVertex(v, false);
1590     }
1591
1592     public URI getURIForVertex(Vertex v, boolean overwrite) {
1593         URI uri = UriBuilder.fromPath("/unknown-uri").build();
1594
1595         String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1596
1597         if (aaiUri != null && !overwrite) {
1598             uri = UriBuilder.fromPath(aaiUri).build();
1599         }
1600
1601         return uri;
1602     }
1603
1604
1605     public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1606             throws AAIUnknownObjectException {
1607
1608         Introspector obj;
1609
1610         try {
1611             obj = this.loader.introspectorFromName(cousinType);
1612         } catch (AAIUnknownObjectException ex) {
1613             if (LOGGER.isTraceEnabled()) {
1614                 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1615                         cousinType, cousinVertex.id());
1616             }
1617             return;
1618         }
1619
1620         String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1621         List<Introspector> relatedToProperties = new ArrayList<>();
1622
1623         if (nameProps != null) {
1624             String[] props = nameProps.split(",");
1625             for (String prop : props) {
1626                 final Object temp = getProperty(obj, prop, cousinVertex);
1627                 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1628                 relatedTo.setValue("property-key", cousinType + "." + prop);
1629                 relatedTo.setValue("property-value", temp);
1630                 relatedToProperties.add(relatedTo);
1631             }
1632         }
1633
1634         if (!relatedToProperties.isEmpty()) {
1635             List<Object> relatedToList = relationship.getValue("related-to-property");
1636             for (Introspector introspector : relatedToProperties) {
1637                 relatedToList.add(introspector.getUnderlyingObject());
1638             }
1639         }
1640
1641     }
1642
1643     private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1644
1645         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1646         String dbPropertyName = prop;
1647
1648         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1649             dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1650         }
1651
1652         return vertex.<Object>property(dbPropertyName).orElse(null);
1653     }
1654
1655     /**
1656      * Creates the edge.
1657      *
1658      * @param relationship the relationship
1659      * @param inputVertex the input vertex
1660      * @return true, if successful
1661      * @throws UnsupportedEncodingException the unsupported encoding exception
1662      * @throws AAIException the AAI exception
1663      */
1664     public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1665             throws UnsupportedEncodingException, AAIException {
1666
1667         Vertex relatedVertex;
1668         StopWatch.conditionalStart();
1669         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1670
1671         String label = null;
1672         if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1673             label = relationship.getValue(RELATIONSHIP_LABEL);
1674         }
1675
1676         List<Vertex> results = parser.getQueryBuilder().toList();
1677         if (results.isEmpty()) {
1678             dbTimeMsecs += StopWatch.stopIfStarted();
1679             AAIException e = new AAIException(AAI_6129,
1680                     "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1681             e.getTemplateVars().add(parser.getResultType());
1682             e.getTemplateVars().add(parser.getUri().toString());
1683             throw e;
1684         } else {
1685             // still an issue if there's more than one
1686             relatedVertex = results.get(0);
1687         }
1688
1689         if (relatedVertex != null) {
1690
1691             Edge e;
1692             try {
1693                 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1694                 if (e == null) {
1695                     e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1696                     if(isDeltaEventsEnabled) {
1697                         deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1698                     }
1699                 } else {
1700                     // attempted to link two vertexes already linked
1701                 }
1702             } finally {
1703                 dbTimeMsecs += StopWatch.stopIfStarted();
1704             }
1705         }
1706
1707         dbTimeMsecs += StopWatch.stopIfStarted();
1708         return relatedVertex;
1709     }
1710
1711     /**
1712      * Gets all the edges between of the type with the specified label.
1713      *
1714      * @param aVertex the out vertex
1715      * @param bVertex the in vertex
1716      * @return the edges between
1717      */
1718     private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1719
1720         Edge result = null;
1721
1722         if (bVertex != null) {
1723             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1724             if (EdgeType.TREE.equals(type)) {
1725                 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1726                 if (edgeRule.getDirection().equals(Direction.IN)) {
1727                     findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1728                             .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1729                             .not(__.has(EdgeField.PRIVATE.toString(), true));
1730                 } else {
1731                     findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1732                             .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1733                             .not(__.has(EdgeField.PRIVATE.toString(), true));
1734                 }
1735                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1736             } else {
1737                 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1738                 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1739                         .not(__.has(EdgeField.PRIVATE.toString(), true));
1740                 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1741             }
1742             List<Edge> list = findEdgesBetween.toList();
1743             if (!list.isEmpty()) {
1744                 result = list.get(0);
1745             }
1746         }
1747
1748         return result;
1749     }
1750
1751     /**
1752      * Gets all the edges string between of the type.
1753      *
1754      * @param aVertex the out vertex
1755      * @param bVertex the in vertex
1756      * @return the edges between
1757      * @throws NoEdgeRuleFoundException
1758      */
1759     private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1760
1761         List<String> result = new ArrayList<>();
1762
1763         if (bVertex != null) {
1764             GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1765             findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1766             if (EdgeType.TREE.equals(type)) {
1767                 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1768                         __.has(EdgeField.PRIVATE.toString(), true)));
1769             } else {
1770                 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1771                         .not(__.has(EdgeField.PRIVATE.toString(), true));
1772             }
1773             findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1774             result = findEdgesBetween.label().toList();
1775         }
1776         return result;
1777     }
1778
1779     /**
1780      * Gets all the edges between the vertexes with the label and type.
1781      *
1782      * @param aVertex the out vertex
1783      * @param bVertex the in vertex
1784      * @param label
1785      * @return the edges between
1786      * @throws AAIException the AAI exception
1787      */
1788     private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1789
1790         Edge edge = null;
1791
1792         if (bVertex != null) {
1793             String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1794             String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1795             EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1796             EdgeRule rule;
1797             try {
1798                 rule = edgeRules.getRule(q);
1799             } catch (EdgeRuleNotFoundException e) {
1800                 throw new NoEdgeRuleFoundException(e);
1801             } catch (AmbiguousRuleChoiceException e) {
1802                 throw new MultipleEdgeRuleFoundException(e);
1803             }
1804             edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1805         }
1806
1807         return edge;
1808     }
1809
1810     /**
1811      * Gets the edge between with the label and edge type.
1812      *
1813      * @param aVertex the out vertex
1814      * @param bVertex the in vertex
1815      * @param label
1816      * @return the edge between
1817      * @throws AAIException the AAI exception
1818      * @throws NoEdgeRuleFoundException
1819      */
1820     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1821
1822         StopWatch.conditionalStart();
1823         if (bVertex != null) {
1824
1825             Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1826             if (edge != null) {
1827                 dbTimeMsecs += StopWatch.stopIfStarted();
1828                 return edge;
1829             }
1830
1831         }
1832         dbTimeMsecs += StopWatch.stopIfStarted();
1833         return null;
1834     }
1835
1836     public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1837         return this.getEdgeBetween(type, aVertex, bVertex, null);
1838     }
1839
1840     /**
1841      * Delete edge.
1842      *
1843      * @param relationship the relationship
1844      * @param inputVertex the input vertex
1845      * @return true, if successful
1846      * @throws UnsupportedEncodingException the unsupported encoding exception
1847      * @throws AAIException the AAI exception
1848      */
1849     public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1850             throws UnsupportedEncodingException, AAIException {
1851
1852         Vertex relatedVertex;
1853         StopWatch.conditionalStart();
1854         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1855
1856         List<Vertex> results = parser.getQueryBuilder().toList();
1857
1858         String label = null;
1859         if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1860             label = relationship.getValue(RELATIONSHIP_LABEL);
1861         }
1862
1863         if (results.isEmpty()) {
1864             dbTimeMsecs += StopWatch.stopIfStarted();
1865             return Optional.empty();
1866         }
1867
1868         relatedVertex = results.get(0);
1869         Edge edge;
1870         try {
1871             edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1872         } catch (NoEdgeRuleFoundException e) {
1873             dbTimeMsecs += StopWatch.stopIfStarted();
1874             throw new AAIException(AAI_6129, e);
1875         }
1876         if (edge != null) {
1877             if(isDeltaEventsEnabled) {
1878                 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1879                 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1880             }
1881             edge.remove();
1882             dbTimeMsecs += StopWatch.stopIfStarted();
1883             return Optional.of(relatedVertex);
1884         } else {
1885             dbTimeMsecs += StopWatch.stopIfStarted();
1886             return Optional.empty();
1887         }
1888
1889     }
1890
1891     /**
1892      * Delete with traversal.
1893      *
1894      * @param startVertex the start vertex
1895      */
1896     public void deleteWithTraversal(Vertex startVertex) {
1897         StopWatch.conditionalStart();
1898         List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1899         this.delete(results);
1900     }
1901
1902     /**
1903      * Removes the list of vertexes from the graph
1904      * <p>
1905      * Current the vertex label will just be vertex but
1906      * in the future the aai-node-type property will be replaced
1907      * by using the vertex label as when retrieving an vertex
1908      * and retrieving an single property on an vertex will pre-fetch
1909      * all the properties of that vertex and this is due to the following property
1910      * <p>
1911      * <code>
1912      * query.fast-property=true
1913      * </code>
1914      * <p>
1915      * JanusGraph doesn't provide the capability to override that
1916      * at a transaction level and there is a plan to move to vertex label
1917      * so it is best to utilize this for now and when the change is applied
1918      *
1919      * @param vertices - list of vertices to delete from the graph
1920      */
1921     void delete(List<Vertex> vertices) {
1922         StopWatch.conditionalStart();
1923
1924         for (Vertex v : vertices) {
1925             LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
1926             if(isDeltaEventsEnabled) {
1927                 deltaForVertexDelete(v);
1928             }
1929             //add the cousin vertexes of v to have their resource-version updated and notified on.
1930             v.edges(Direction.BOTH)
1931                 .forEachRemaining(e -> {
1932                     if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
1933                         && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
1934                         e.bothVertices().forEachRemaining(cousinV -> {
1935                             if (!v.equals(cousinV)) {
1936                                 edgeVertexes.add(cousinV);
1937                             }
1938                         });
1939                     }
1940                 });
1941
1942             //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
1943             // it should be removed from them as v is to be deleted
1944             edgeVertexes.remove(v);
1945             updatedVertexes.remove(v);
1946             v.remove();
1947         }
1948
1949         dbTimeMsecs += StopWatch.stopIfStarted();
1950     }
1951
1952     private void deltaForVertexDelete(Vertex v) {
1953         String aaiUri = v.property(AAIProperties.AAI_URI).value().toString();
1954         v.keys().forEach(k -> {
1955             List<Object> list = new ArrayList<>();
1956             v.properties(k).forEachRemaining(vp -> list.add(vp.value()));
1957             if (list.size() == 1) {
1958                 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)), DeltaAction.DELETE);
1959             } else {
1960                 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE);
1961             }
1962
1963         });
1964         v.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
1965     }
1966
1967     /**
1968      * Delete.
1969      *
1970      * @param v the v
1971      * @param resourceVersion the resource version
1972      * @throws IllegalArgumentException the illegal argument exception
1973      * @throws AAIException the AAI exception
1974      * @throws InterruptedException the interrupted exception
1975      */
1976     public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
1977             throws IllegalArgumentException, AAIException {
1978
1979         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1980         /*
1981          * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1982          * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
1983          * So its better to make these in 2 steps
1984          */
1985         if (result && !deletableVertices.isEmpty()) {
1986             result = verifyPreventDeleteSemantics(deletableVertices);
1987         }
1988         if (result) {
1989
1990             try {
1991                 deleteWithTraversal(v);
1992             } catch (IllegalStateException e) {
1993                 throw new AAIException("AAI_6110", e);
1994             }
1995
1996         }
1997     }
1998
1999     /**
2000      * Delete.
2001      *
2002      * @param v the v
2003      * @param resourceVersion the resource version
2004      * @throws IllegalArgumentException the illegal argument exception
2005      * @throws AAIException the AAI exception
2006      * @throws InterruptedException the interrupted exception
2007      */
2008     public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion)
2009             throws IllegalArgumentException, AAIException {
2010
2011         boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2012
2013         if (result) {
2014
2015             try {
2016                 deleteWithTraversal(v);
2017             } catch (IllegalStateException e) {
2018                 throw new AAIException("AAI_6110", e);
2019             }
2020
2021         }
2022     }
2023
2024     /**
2025      * Verify delete semantics.
2026      *
2027      * @param vertex the vertex
2028      * @param resourceVersion the resource version
2029      * @return true, if successful
2030      * @throws AAIException the AAI exception
2031      */
2032     private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2033             throws AAIException {
2034         boolean result;
2035         String nodeType;
2036         nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2037         if (enableResourceVersion) {
2038             this.verifyResourceVersion("delete", nodeType,
2039                 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2040         }
2041         List<Vertex> vertices = new ArrayList<>();
2042         vertices.add(vertex);
2043         result = verifyPreventDeleteSemantics(vertices);
2044
2045         return result;
2046     }
2047
2048     /**
2049      * Verify Prevent delete semantics.
2050      *
2051      * @param vertices the list of vertices
2052      * @return true, if successful
2053      * @throws AAIException the AAI exception
2054      */
2055     private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2056         boolean result = true;
2057         String errorDetail = " unknown delete semantic found";
2058         String aaiExceptionCode = "";
2059
2060         StopWatch.conditionalStart();
2061         /*
2062          * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2063          * "prevent-delete" condition
2064          * If yes - that should prevent the deletion of the vertex
2065          * Dedup makes sure we dont capture the prevent-delete vertices twice
2066          * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2067          */
2068
2069         List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2070                 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2071                         .values(AAIProperties.NODE_TYPE),
2072                         __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2073                                 .values(AAIProperties.NODE_TYPE))
2074                 .dedup().toList();
2075
2076         dbTimeMsecs += StopWatch.stopIfStarted();
2077         if (!preventDeleteVertices.isEmpty()) {
2078             aaiExceptionCode = "AAI_6110";
2079             errorDetail = String.format(
2080                     "Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s",
2081                     preventDeleteVertices);
2082             result = false;
2083         }
2084         if (!result) {
2085             throw new AAIException(aaiExceptionCode, errorDetail);
2086         }
2087         return result;
2088     }
2089
2090     /**
2091      * Verify resource version.
2092      *
2093      * @param action the action
2094      * @param nodeType the node type
2095      * @param currentResourceVersion the current resource version
2096      * @param resourceVersion the resource version
2097      * @param uri the uri
2098      * @return true, if successful
2099      * @throws AAIException the AAI exception
2100      */
2101     public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2102             String resourceVersion, String uri) throws AAIException {
2103         String enabled = "";
2104         String errorDetail = "";
2105         String aaiExceptionCode = "";
2106         boolean isDeleteResourceVersionOk = true;
2107         if (currentResourceVersion == null) {
2108             currentResourceVersion = "";
2109         }
2110
2111         if (resourceVersion == null) {
2112             resourceVersion = "";
2113         }
2114         try {
2115             enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2116
2117         } catch (AAIException e) {
2118             ErrorLogHelper.logException(e);
2119         }
2120         if (enabled.equals("true")) {
2121             if ("delete".equals(action)) {
2122                 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2123             }
2124             if ((!isDeleteResourceVersionOk)
2125                     || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2126                 if ("create".equals(action) && !resourceVersion.equals("")) {
2127                     errorDetail = "resource-version passed for " + action + " of " + uri;
2128                     aaiExceptionCode = "AAI_6135";
2129                 } else if (resourceVersion.equals("")) {
2130                     errorDetail = "resource-version not passed for " + action + " of " + uri;
2131                     aaiExceptionCode = "AAI_6130";
2132                 } else {
2133                     errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2134                     aaiExceptionCode = "AAI_6131";
2135                 }
2136
2137                 throw new AAIException(aaiExceptionCode, errorDetail);
2138
2139             }
2140         }
2141         return true;
2142     }
2143
2144     /**
2145      * Verify resource version for delete.
2146      *
2147      * @param currentResourceVersion the current resource version
2148      * @param resourceVersion the resource version
2149      * @return true, if successful or false if there is a mismatch
2150      */
2151     private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2152
2153         boolean isDeleteResourceVersionOk = true;
2154         String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2155                 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2156
2157         if ((!currentResourceVersion.equals(resourceVersion))
2158                 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2159             isDeleteResourceVersionOk = false;
2160         }
2161         return isDeleteResourceVersionOk;
2162     }
2163
2164     /**
2165      * Convert from camel case.
2166      *
2167      * @param name the name
2168      * @return the string
2169      */
2170     private String convertFromCamelCase(String name) {
2171         String result = "";
2172         result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2173
2174         NamingExceptions exceptions = NamingExceptions.getInstance();
2175         result = exceptions.getDBName(result);
2176
2177         return result;
2178     }
2179
2180     private boolean canModify(Introspector obj, String propName, String requestContext) {
2181         final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2182         if (readOnly != null) {
2183             final String[] items = readOnly.split(",");
2184             for (String item : items) {
2185                 if (requestContext.equals(item)) {
2186                     return false;
2187                 }
2188             }
2189         }
2190         return true;
2191     }
2192
2193     private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2194
2195         SideEffectRunner runner = new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class)
2196                 .addSideEffect(PrivateEdge.class).build();
2197
2198         runner.execute(obj, self);
2199     }
2200
2201     private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2202
2203         SideEffectRunner runner =
2204                 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2205
2206         runner.execute(obj, self);
2207     }
2208
2209     private void enrichData(Introspector obj, Vertex self) throws AAIException {
2210
2211         SideEffectRunner runner =
2212                 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
2213
2214         runner.execute(obj, self);
2215     }
2216
2217     public double getDBTimeMsecs() {
2218         return (dbTimeMsecs);
2219     }
2220
2221     /**
2222      * Db to object With Filters
2223      * This is for a one-time run with Tenant Isloation to only filter relationships
2224      *
2225      * @param obj the obj
2226      * @param v the vertex from the graph
2227      * @param depth the depth
2228      * @param nodeOnly specify if to exclude relationships or not
2229      * @param filterCousinNodes
2230      * @return the introspector
2231      * @throws AAIException the AAI exception
2232      * @throws IllegalAccessException the illegal access exception
2233      * @throws IllegalArgumentException the illegal argument exception
2234      * @throws InvocationTargetException the invocation target exception
2235      * @throws SecurityException the security exception
2236      * @throws InstantiationException the instantiation exception
2237      * @throws NoSuchMethodException the no such method exception
2238      * @throws UnsupportedEncodingException the unsupported encoding exception
2239      * @throws MalformedURLException the malformed URL exception
2240      * @throws AAIUnknownObjectException
2241      * @throws URISyntaxException
2242      */
2243     public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2244                                               List<String> filterCousinNodes, List<String> filterParentNodes)
2245         throws AAIException, UnsupportedEncodingException {
2246         return dbToObjectWithFilters(obj, v, seen, depth, nodeOnly,
2247         filterCousinNodes, filterParentNodes, false);
2248     }
2249
2250     /**
2251      * Db to object With Filters
2252      * This is for a one-time run with Tenant Isloation to only filter relationships
2253      * TODO: Chnage the original dbToObject to take filter parent/cousins
2254      *
2255      * @param obj the obj
2256      * @param v the vertex from the graph
2257      * @param depth the depth
2258      * @param nodeOnly specify if to exclude relationships or not
2259      * @param filterCousinNodes
2260      * @param isSkipRelatedTo determine to incorporated related-to-property data
2261      * @return the introspector
2262      * @throws AAIException the AAI exception
2263      * @throws IllegalAccessException the illegal access exception
2264      * @throws IllegalArgumentException the illegal argument exception
2265      * @throws InvocationTargetException the invocation target exception
2266      * @throws SecurityException the security exception
2267      * @throws InstantiationException the instantiation exception
2268      * @throws NoSuchMethodException the no such method exception
2269      * @throws UnsupportedEncodingException the unsupported encoding exception
2270      * @throws MalformedURLException the malformed URL exception
2271      * @throws AAIUnknownObjectException
2272      * @throws URISyntaxException
2273      */
2274     // TODO - See if you can merge the 2 dbToObjectWithFilters
2275     public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2276             List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2277             throws AAIException, UnsupportedEncodingException {
2278         String cleanUp = FALSE;
2279         if (depth < 0) {
2280             return null;
2281         }
2282         depth--;
2283         seen.add(v);
2284         boolean modified = false;
2285         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2286             List<Object> getList = null;
2287
2288             if (!(obj.isComplexType(property) || obj.isListType(property))) {
2289                 this.copySimpleProperty(property, obj, v);
2290                 modified = true;
2291             } else {
2292                 if (obj.isComplexType(property)) {
2293                     /* container case */
2294
2295                     if (!property.equals("relationship-list") && depth >= 0) {
2296                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2297                         Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly,
2298                                 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2299                         if (result != null) {
2300                             obj.setValue(property, argumentObject.getUnderlyingObject());
2301                             modified = true;
2302                         }
2303                     } else if (property.equals("relationship-list") && !nodeOnly) {
2304                         /* relationships need to be handled correctly */
2305                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2306                         relationshipList =
2307                                 createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes, isSkipRelatedTo);
2308                         if (relationshipList != null) {
2309                             obj.setValue(property, relationshipList.getUnderlyingObject());
2310                             modified = true;
2311                         }
2312
2313                     }
2314                 } else if (obj.isListType(property)) {
2315
2316                     if (property.equals("any")) {
2317                         continue;
2318                     }
2319                     String genericType = obj.getGenericTypeClass(property).getSimpleName();
2320                     if (obj.isComplexGenericType(property) && depth >= 0) {
2321                         final String childDbName = convertFromCamelCase(genericType);
2322                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2323                         EdgeRule rule;
2324
2325                         boolean isThisParentRequired =
2326                                 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2327
2328                         EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
2329
2330                         try {
2331                             rule = edgeRules.getRule(q);
2332                         } catch (EdgeRuleNotFoundException e) {
2333                             throw new NoEdgeRuleFoundException(e);
2334                         } catch (AmbiguousRuleChoiceException e) {
2335                             throw new MultipleEdgeRuleFoundException(e);
2336                         }
2337                         if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2338                             Direction ruleDirection = rule.getDirection();
2339                             List<Vertex> verticesList = new ArrayList<>();
2340                             v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2341                                 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2342                                     verticesList.add(vertex);
2343                                 }
2344                             });
2345                             if (!verticesList.isEmpty()) {
2346                                 getList = obj.getValue(property);
2347                             }
2348                             int processed = 0;
2349                             for (Vertex childVertex : verticesList) {
2350                                 if (!seen.contains(childVertex)) {
2351                                     Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2352
2353                                     Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2354                                             nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2355                                     if (result != null) {
2356                                         getList.add(argumentObject.getUnderlyingObject());
2357                                     }
2358
2359                                     processed++;
2360                                 } else {
2361                                     LOGGER.warn("Cycle found while serializing vertex id={}",
2362                                             childVertex.id().toString());
2363                                 }
2364                             }
2365                             if (processed == 0) {
2366                                 // vertices were all seen, reset the list
2367                                 getList = null;
2368                             }
2369                             if (processed > 0) {
2370                                 modified = true;
2371                             }
2372                         }
2373                     } else if (obj.isSimpleGenericType(property)) {
2374                         List<Object> temp = this.engine.getListProperty(v, property);
2375                         if (temp != null) {
2376                             getList = obj.getValue(property);
2377                             getList.addAll(temp);
2378                             modified = true;
2379                         }
2380
2381                     }
2382
2383                 }
2384
2385             }
2386         }
2387
2388         // no changes were made to this obj, discard the instance
2389         if (!modified) {
2390             return null;
2391         }
2392         this.enrichData(obj, v);
2393         return obj;
2394
2395     }
2396
2397     /**
2398      * Creates the relationship list with the filtered node types.
2399      *
2400      * @param v the v
2401      * @param obj the obj
2402      * @param cleanUp the clean up
2403      * @return the object
2404      * @throws IllegalArgumentException the illegal argument exception
2405      * @throws SecurityException the security exception
2406      * @throws UnsupportedEncodingException the unsupported encoding exception
2407      * @throws AAIException the AAI exception
2408      */
2409     private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp,
2410             List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2411         List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2412
2413         Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2414             String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2415             return filterNodes.parallelStream().anyMatch(node::contains);
2416         }).iterator();
2417
2418         List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2419
2420         List<Vertex> cousins = new ArrayList<>();
2421         cousinVertices.forEachRemaining(cousins::add);
2422         for (Vertex cousin : cousins) {
2423
2424             Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2425             Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2426             if (result != null) {
2427                 relationshipObjList.add(result);
2428             }
2429
2430         }
2431
2432         if (relationshipObjList.isEmpty()) {
2433             return null;
2434         } else {
2435             return obj;
2436         }
2437     }
2438
2439     public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2440         this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2441         return this.edgeVertexes;
2442     }
2443
2444     public void addVertexToEdgeVertexes(Vertex vertex){
2445         this.edgeVertexes.add(vertex);
2446     }
2447
2448     private String urlToUri(String url) {
2449         if (url.startsWith("/")) {
2450             url = url.substring(1);
2451         }
2452
2453         if (url.endsWith("/")) {
2454             url = url.substring(0, url.length() - 1);
2455         }
2456
2457         // TODO - Check if this makes to do for model driven for base uri path
2458         url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2459         if (url.charAt(0) != '/') {
2460             url = '/' + url;
2461         }
2462
2463         return url;
2464     }
2465
2466 }