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