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