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