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