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