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