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