Update the license for 2017-2018 license
[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.thinkaurelius.titan.core.SchemaViolationException;
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.javatuples.Pair;
33 import org.javatuples.Triplet;
34 import org.onap.aai.db.props.AAIProperties;
35 import org.onap.aai.exceptions.AAIException;
36 import org.onap.aai.introspection.*;
37 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
38 import org.onap.aai.introspection.sideeffect.DataCopy;
39 import org.onap.aai.introspection.sideeffect.DataLinkReader;
40 import org.onap.aai.introspection.sideeffect.DataLinkWriter;
41 import org.onap.aai.introspection.sideeffect.SideEffectRunner;
42 import org.onap.aai.logging.ErrorLogHelper;
43 import org.onap.aai.logging.LogFormatTools;
44 import org.onap.aai.parsers.query.QueryParser;
45 import org.onap.aai.parsers.uri.URIParser;
46 import org.onap.aai.parsers.uri.URIToRelationshipObject;
47 import org.onap.aai.query.builder.QueryBuilder;
48 import org.onap.aai.schema.enums.ObjectMetadata;
49 import org.onap.aai.schema.enums.PropertyMetadata;
50 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
51 import org.onap.aai.serialization.db.util.VersionChecker;
52 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
53 import org.onap.aai.serialization.tinkerpop.TreeBackedVertex;
54 import org.onap.aai.util.AAIApiServerURLBase;
55 import org.onap.aai.util.AAIConfig;
56 import org.onap.aai.util.AAIConstants;
57 import org.onap.aai.workarounds.NamingExceptions;
58 import org.onap.aai.logging.StopWatch;
59
60 import javax.ws.rs.core.UriBuilder;
61 import java.io.UnsupportedEncodingException;
62 import java.lang.reflect.Array;
63 import java.lang.reflect.InvocationTargetException;
64 import java.net.MalformedURLException;
65 import java.net.URI;
66 import java.net.URISyntaxException;
67 import java.util.*;
68 import java.util.concurrent.Callable;
69 import java.util.concurrent.ExecutionException;
70 import java.util.concurrent.ExecutorService;
71 import java.util.concurrent.Future;
72 import java.util.regex.Matcher;
73 import java.util.regex.Pattern;
74
75 public class DBSerializer {
76         
77         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
78         
79         private final TransactionalGraphEngine engine;
80         private final String sourceOfTruth;
81         private final ModelType introspectionType;
82         private final Version version;
83         private final Loader latestLoader;
84         private final EdgeRules edgeRules = EdgeRules.getInstance();
85         private final Loader loader;
86         private final String baseURL;
87         private double dbTimeMsecs = 0;
88         /**
89          * Instantiates a new DB serializer.
90          *
91          * @param version the version
92          * @param engine the engine
93          * @param g the g
94          * @param introspectionType the introspection type
95          * @param sourceOfTruth the source of truth
96          * @param llBuilder the ll builder
97          * @throws AAIException 
98          */
99         public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
100                 this.engine = engine;
101                 this.sourceOfTruth = sourceOfTruth;
102                 this.introspectionType = introspectionType;
103                 this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST);
104                 this.version = version;
105                 this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version);
106                 this.baseURL = AAIApiServerURLBase.get(version);
107         }
108         
109         /**
110          * Touch standard vertex properties.
111          *
112          * @param v the v
113          * @param isNewVertex the is new vertex
114          */
115         public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
116                 
117                 String timeNowInSec = Long.toString(System.currentTimeMillis());
118                 if (isNewVertex) {
119                         v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
120                         v.property(AAIProperties.CREATED_TS, timeNowInSec);
121                         v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString());
122                 }
123                 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
124                 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
125                 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
126                 
127         }
128         
129         private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
130                 
131                 v.property(AAIProperties.NODE_TYPE, nodeType);
132                 touchStandardVertexProperties(v, isNewVertex);
133
134         }
135         
136         
137         
138         /**
139          * Creates the new vertex.
140          *
141          * @param wrappedObject the wrapped object
142          * @return the vertex
143          * @throws UnsupportedEncodingException the unsupported encoding exception
144          * @throws AAIException the AAI exception
145          */
146         public Vertex createNewVertex(Introspector wrappedObject) {
147                 Vertex v;
148                 try {
149                         StopWatch.conditionalStart();
150                         v = this.engine.tx().addVertex();
151                         touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
152                 }
153                 finally {
154                         dbTimeMsecs += StopWatch.stopIfStarted();
155                 }
156                 return v;
157         }
158         
159         /**
160          * Trim class name.
161          *
162          * @param className the class name
163          * @return the string
164          */
165         /*
166          * Removes the classpath from a class name
167          */
168         public String trimClassName (String className) {
169                 String returnValue = "";
170                 
171                 if (className.lastIndexOf('.') == -1) {
172                         return className;
173                 }
174                 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
175                 
176                 return returnValue;
177         }
178         
179         /**
180          * Serialize to db.
181          *
182          * @param obj the obj
183          * @param v the v
184          * @param uriQuery the uri query
185          * @param identifier the identifier
186          * @throws SecurityException the security exception
187          * @throws IllegalAccessException the illegal access exception
188          * @throws IllegalArgumentException the illegal argument exception
189          * @throws InvocationTargetException the invocation target exception
190          * @throws InstantiationException the instantiation exception
191          * @throws InterruptedException the interrupted exception
192          * @throws NoSuchMethodException the no such method exception
193          * @throws AAIException the AAI exception
194          * @throws UnsupportedEncodingException the unsupported encoding exception
195          * @throws AAIUnknownObjectException 
196          */
197         public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
198                 StopWatch.conditionalStart();
199                 try {
200                         if (uriQuery.isDependent()) {
201                                 //try to find the parent
202                                 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
203                                 if (!vertices.isEmpty()) {
204                                         Vertex parent = vertices.get(0);
205                                         this.reflectDependentVertex(parent, v, obj, requestContext);
206                                 } else {
207                                         dbTimeMsecs += StopWatch.stopIfStarted();
208                                         throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
209                                 }
210                         } else {
211                                 serializeSingleVertex(v, obj, requestContext);
212                         }
213
214                 } catch (SchemaViolationException e) {
215                         dbTimeMsecs += StopWatch.stopIfStarted();
216                         throw new AAIException("AAI_6117", e);
217                 }
218                 dbTimeMsecs += StopWatch.stopIfStarted();
219         }
220         
221         public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
222                 StopWatch.conditionalStart();
223                 try {
224                         boolean isTopLevel = obj.isTopLevel();
225                         if (isTopLevel) {
226                                 v.property(AAIProperties.AAI_URI, obj.getURI());
227                         }
228                         
229                         processObject(obj, v, requestContext);
230                         if (!isTopLevel) {
231                                 URI uri = this.getURIForVertex(v);
232                                 URIParser parser = new URIParser(this.loader, uri);
233                                 if (parser.validate()) {
234                                         VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
235                                         if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
236                                                 v.property(AAIProperties.AAI_URI, uri.toString());
237                                         }
238                                 }
239                         }
240                 } catch (SchemaViolationException e) {
241                         throw new AAIException("AAI_6117", e);
242                 }
243                 finally {
244                         dbTimeMsecs += StopWatch.stopIfStarted();
245                 }
246         }
247         
248         /**
249          * Process object.
250          *
251          * @param <T> the generic type
252          * @param obj the obj
253          * @param v the v
254          * @return the list
255          * @throws IllegalAccessException the illegal access exception
256          * @throws IllegalArgumentException the illegal argument exception
257          * @throws InvocationTargetException the invocation target exception
258          * @throws InstantiationException the instantiation exception
259          * @throws NoSuchMethodException the no such method exception
260          * @throws SecurityException the security exception
261          * @throws AAIException the AAI exception
262          * @throws UnsupportedEncodingException the unsupported encoding exception
263          * @throws AAIUnknownObjectException 
264          */
265         /*
266          * Helper method for reflectToDb
267          * Handles all the property setting
268          */
269         private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
270                 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
271                 properties.remove(AAIProperties.RESOURCE_VERSION);
272                 List<Vertex> dependentVertexes = new ArrayList<>();
273                 List<Vertex> processedVertexes = new ArrayList<>();
274                 boolean isComplexType = false;
275                 boolean isListType = false;
276                 if (!obj.isContainer()) {
277                         this.touchStandardVertexProperties(obj.getDbName(), v, false);
278                 }
279                 this.executePreSideEffects(obj, v);
280                 for (String property : properties) {
281                         Object value = null;
282                         final String propertyType;
283                         propertyType = obj.getType(property);
284                         isComplexType = obj.isComplexType(property);
285                         isListType = obj.isListType(property);
286                         value = obj.getValue(property);
287
288                         if (!(isComplexType || isListType)) {
289                                 boolean canModify = this.canModify(obj, property, requestContext);
290                                 
291                                 if (canModify) {
292                                         final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
293                                         String dbProperty = property;
294                                         if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
295                                                 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
296                                         }
297                                         if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
298                                                 //data linked properties are ephemeral
299                                                 //they are populated dynamically on GETs
300                                                 continue;
301                                         }
302                                         if (value != null) {
303                                                 if (!value.equals(v.property(dbProperty).orElse(null))) {
304                                                         if (propertyType.toLowerCase().contains(".long")) {
305                                                                 v.property(dbProperty, new Integer(((Long)value).toString()));
306                                                         } else {
307                                                                 v.property(dbProperty, value);
308                                                         }
309                                                 }
310                                         } else {
311                                                 v.property(dbProperty).remove();
312                                         }
313                                 }
314                         } else if (isListType) {
315                                 List<Object> list = (List<Object>)value;
316                                 if (obj.isComplexGenericType(property)) {
317                                         if (list != null) {
318                                                 for (Object o : list) {
319                                                         Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
320                                                         child.setURIChain(obj.getURI());
321                                                         processedVertexes.add(reflectDependentVertex(v, child, requestContext));
322                                                 }
323                                         }
324                                 } else {
325                                         //simple list case
326                                         engine.setListProperty(v, property, list);
327                                 }
328                         } else {
329                                 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
330                                 if (value != null) { //effectively ignore complex properties not included in the object we're processing
331                                         if (value.getClass().isArray()) {
332                                                 
333                                                 int length = Array.getLength(value);
334                                             for (int i = 0; i < length; i ++) {
335                                                 Object arrayElement = Array.get(value, i);
336                                                 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
337                                                         child.setURIChain(obj.getURI());
338                                                         processedVertexes.add(reflectDependentVertex(v, child, requestContext));
339
340                                             }
341                                         } else if (!property.equals("relationship-list")) {
342                                                 // container case
343                                                 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
344                                                 if (introspector.isContainer()) {
345                                                         dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
346                                                         introspector.setURIChain(obj.getURI());
347                                                         
348                                                         processedVertexes.addAll(processObject(introspector, v, requestContext));
349
350                                                 } else {
351                                                         dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
352                                                         processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
353
354                                                 }
355                                         } else if (property.equals("relationship-list")) {
356                                                 handleRelationships(obj, v);
357                                         }
358                                 }
359                         }
360                 }
361                 this.writeThroughDefaults(v, obj);
362                 /* handle those vertexes not touched */
363                 for (Vertex toBeRemoved : processedVertexes) {
364                         dependentVertexes.remove(toBeRemoved);
365                 }
366                 this.deleteItemsWithTraversal(dependentVertexes);
367                 
368                 this.executePostSideEffects(obj, v);
369                 return processedVertexes;
370         }
371         
372         /**
373          * Handle relationships.
374          *
375          * @param obj the obj
376          * @param vertex the vertex
377          * @throws SecurityException the security exception
378          * @throws IllegalAccessException the illegal access exception
379          * @throws IllegalArgumentException the illegal argument exception
380          * @throws InvocationTargetException the invocation target exception
381          * @throws UnsupportedEncodingException the unsupported encoding exception
382          * @throws AAIException the AAI exception
383          */
384         /*
385          * Handles the explicit relationships defined for an obj
386          */
387         private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
388                 
389         
390         
391                 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
392                 processRelationshipList(wrappedRl, vertex);
393                 
394         
395         }
396         
397         
398         /**
399          * Process relationship list.
400          *
401          * @param wrapped the wrapped
402          * @param v the v
403          * @throws UnsupportedEncodingException the unsupported encoding exception
404          * @throws AAIException the AAI exception
405          */
406         private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
407                                 
408                 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
409                 
410                 List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
411                 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
412                 
413                 for (Object relationship : relationships) {
414                         Edge e = null;
415                         Vertex cousinVertex = null;
416                         String label = null;
417                         Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
418                         QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
419                         
420                         if (wrappedRel.hasProperty("relationship-label")) {
421                                 label = wrappedRel.getValue("relationship-label");
422                         }
423                         
424                         List<Vertex> results = parser.getQueryBuilder().toList();
425                         if (results.isEmpty()) {
426                                 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
427                                 ex.getTemplateVars().add(parser.getResultType());
428                                 ex.getTemplateVars().add(parser.getUri().toString());
429                                 throw ex;
430                         } else { 
431                                 //still an issue if there's more than one
432                                 cousinVertex = results.get(0);
433                         }
434                         
435                         if (cousinVertex != null) {
436                                         
437                                 if (!edgeRules.hasEdgeRule(v, cousinVertex, label)) {
438                                         throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " 
439                                                         + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"")  +"."); 
440                                 } else if (edgeRules.hasTreeEdgeRule(v, cousinVertex) && !edgeRules.hasCousinEdgeRule(v, cousinVertex, label)) {
441                                         throw new AAIException("AAI_6145");
442                                 }
443                                 
444                                 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
445
446                                 if (e == null) {
447                                         addEdges.add(new Triplet<>(v, cousinVertex, label));
448                                 } else { 
449                                         existingEdges.remove(e);
450                                 }
451                         }
452                 }
453                 
454                 for (Edge edge : existingEdges) {
455                         edge.remove();
456                 }
457                 for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
458                          try {
459                                 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
460                         } catch (NoEdgeRuleFoundException e) {
461                                 throw new AAIException("AAI_6129", e);
462                         }                       
463                 }
464
465         }
466
467         /**
468          * Write through defaults.
469          *
470          * @param v the v
471          * @param obj the obj
472          * @throws AAIUnknownObjectException 
473          */
474         private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
475                 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
476                 if (latest != null) {
477                         Set<String> required  = latest.getRequiredProperties();
478                         
479                         for (String field : required) {
480                                 String defaultValue = null;
481                                 Object vertexProp = null;
482                                 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
483                                 if (defaultValue != null) {
484                                         vertexProp = v.<Object>property(field).orElse(null);
485                                         if (vertexProp == null) {
486                                                 v.property(field, defaultValue);
487                                         }
488                                 }
489                         }
490                 }
491                 
492         }
493
494         
495         /**
496           * Reflect dependent vertex.
497           *
498           * @param v the v
499           * @param dependentObj the dependent obj
500           * @return the vertex
501           * @throws IllegalAccessException the illegal access exception
502           * @throws IllegalArgumentException the illegal argument exception
503           * @throws InvocationTargetException the invocation target exception
504           * @throws InstantiationException the instantiation exception
505           * @throws NoSuchMethodException the no such method exception
506           * @throws SecurityException the security exception
507           * @throws AAIException the AAI exception
508           * @throws UnsupportedEncodingException the unsupported encoding exception
509          * @throws AAIUnknownObjectException 
510           */
511          private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
512                 
513                 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
514                 //List<Vertex> items = p.getQuery().toList();
515                 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
516                 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
517                 query.createKeyQuery(dependentObj);
518                 
519                 List<Vertex> items = query.toList();
520                 
521                 Vertex dependentVertex = null;
522                 if (items.size() == 1) {
523                         dependentVertex = items.get(0);
524                         this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
525                 } else {
526                         this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
527                         dependentVertex = createNewVertex(dependentObj);
528                 }
529
530                 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
531                                 
532         }
533         
534         /**
535           * Reflect dependent vertex.
536           *
537           * @param parent the parent
538           * @param child the child
539           * @param obj the obj
540           * @return the vertex
541           * @throws IllegalAccessException the illegal access exception
542           * @throws IllegalArgumentException the illegal argument exception
543           * @throws InvocationTargetException the invocation target exception
544           * @throws InstantiationException the instantiation exception
545           * @throws NoSuchMethodException the no such method exception
546           * @throws SecurityException the security exception
547           * @throws AAIException the AAI exception
548           * @throws UnsupportedEncodingException the unsupported encoding exception
549          * @throws AAIUnknownObjectException 
550           */
551          private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
552                 
553                 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
554                 if (parentUri != null) {
555                         String uri;
556                         uri = obj.getURI();
557                         child.property(AAIProperties.AAI_URI, parentUri + uri);
558                 }
559                 processObject(obj, child, requestContext);
560                 
561                 Edge e;
562                 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
563                 if (e == null) {
564                         String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
565                         if (canBeLinked != null && canBeLinked.equals("true")) {
566                                 Loader ldrForCntxt = LoaderFactory.createLoaderForVersion(introspectionType, getVerForContext(requestContext));
567                                 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
568                                 if (isFirst) {
569                                         child.property(AAIProperties.LINKED, true);
570                                 }
571                         }
572                         edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
573                 }
574                 return child;
575                 
576         }
577          
578         private Version getVerForContext(String requestContext) {
579                 Pattern pattern = Pattern.compile("v[0-9]+");
580                 Matcher m = pattern.matcher(requestContext);
581                 if (!m.find()) {
582                         return this.version;
583                 } else {
584                         return Version.valueOf(requestContext);
585                 }
586         }
587          
588         /**
589           * Db to object.
590           *
591           * @param vertices the vertices
592           * @param obj the obj
593           * @param depth the depth
594           * @param cleanUp the clean up
595           * @return the introspector
596           * @throws AAIException the AAI exception
597           * @throws IllegalAccessException the illegal access exception
598           * @throws IllegalArgumentException the illegal argument exception
599           * @throws InvocationTargetException the invocation target exception
600           * @throws SecurityException the security exception
601           * @throws InstantiationException the instantiation exception
602           * @throws NoSuchMethodException the no such method exception
603           * @throws UnsupportedEncodingException the unsupported encoding exception
604           * @throws MalformedURLException the malformed URL exception
605          * @throws AAIUnknownObjectException 
606          * @throws URISyntaxException 
607           */
608          public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
609                 final int internalDepth;
610                 if (depth == Integer.MAX_VALUE) {
611                         internalDepth = depth--;
612                 } else {
613                         internalDepth = depth;
614                 }
615                 StopWatch.conditionalStart();
616                 if (vertices.size() > 1 && !obj.isContainer()) {
617                         dbTimeMsecs += StopWatch.stopIfStarted();
618                         throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
619                 } else if (obj.isContainer()) {
620                         final List getList;
621                         String listProperty = null;
622                         for (String property : obj.getProperties()) {
623                                 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
624                                         listProperty = property;
625                                         break;
626                                 }
627                         }
628                         final String propertyName = listProperty;
629                         getList = (List)obj.getValue(listProperty);
630                         
631                         /* This is an experimental multithreading experiment
632                          * on get alls. 
633                          */
634                         ExecutorService pool = GetAllPool.getInstance().getPool();
635                         
636                         List<Future<Object>> futures = new ArrayList<>();
637                         
638
639                         for (Vertex v : vertices) {
640                                 Callable<Object> task = () -> {
641                                         Set<Vertex> seen = new HashSet<>();
642                                         Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
643                                         Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
644                                         TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
645                                         dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
646                                         return childObject.getUnderlyingObject();
647                                         //getList.add(childObject.getUnderlyingObject());
648                                 };
649                                 futures.add(pool.submit(task));
650                         }
651                         
652                         for (Future<Object> future : futures) {
653                                 try {
654                                         getList.add(future.get());
655                                 } catch (ExecutionException e) {
656                                         dbTimeMsecs += StopWatch.stopIfStarted();
657                                         throw new AAIException("AAI_4000", e);
658                                 } catch (InterruptedException e) {
659                                         dbTimeMsecs += StopWatch.stopIfStarted();
660                                         throw new AAIException("AAI_4000", e);
661                                 }
662                         }
663                 } else if (vertices.size() == 1) {
664                         Set<Vertex> seen = new HashSet<>();
665                         Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
666                         TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
667                         dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
668                 } else {
669                         //obj = null;
670                 }
671                 
672                 dbTimeMsecs += StopWatch.stopIfStarted();
673                 return obj;
674         }
675         
676         /**
677          * Db to object.
678          *
679          * @param obj the obj
680          * @param v the v
681          * @param seen the seen
682          * @param depth the depth
683          * @param cleanUp the clean up
684          * @return the introspector
685          * @throws IllegalAccessException the illegal access exception
686          * @throws IllegalArgumentException the illegal argument exception
687          * @throws InvocationTargetException the invocation target exception
688          * @throws SecurityException the security exception
689          * @throws InstantiationException the instantiation exception
690          * @throws NoSuchMethodException the no such method exception
691          * @throws UnsupportedEncodingException the unsupported encoding exception
692          * @throws AAIException the AAI exception
693          * @throws MalformedURLException the malformed URL exception
694          * @throws AAIUnknownObjectException 
695          * @throws URISyntaxException 
696          */
697         private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
698                 
699                 if (depth < 0) {
700                         return null;
701                 }
702                 depth--;
703                 seen.add(v);
704                 
705                 boolean modified = false;
706                 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
707                         List<Object> getList = null;
708                         Vertex[] vertices = null;
709
710                         if (!(obj.isComplexType(property) || obj.isListType(property))) {
711                                 this.copySimpleProperty(property, obj, v);
712                                 modified = true;
713                         } else {
714                                 if (obj.isComplexType(property)) {
715                                 /* container case */
716         
717                                         if (!property.equals("relationship-list") && depth >= 0) {
718                                                 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
719                                                 Object result  = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
720                                                 if (result != null) {
721                                                         obj.setValue(property, argumentObject.getUnderlyingObject());
722                                                         modified = true;
723                                                 }
724                                         } else if (property.equals("relationship-list") && !nodeOnly){
725                                                 /* relationships need to be handled correctly */
726                                                 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
727                                                 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
728                                                 if (relationshipList != null) {
729                                                         modified = true;
730                                                         obj.setValue(property, relationshipList.getUnderlyingObject());
731                                                         modified = true;
732                                                 }
733                                                 
734                                         }
735                                 } else if (obj.isListType(property)) {
736                                         
737                                         if (property.equals("any")) {
738                                                 continue;
739                                         }
740                                         String genericType = obj.getGenericTypeClass(property).getSimpleName();
741                                         if (obj.isComplexGenericType(property) && depth >= 0) {
742                                                 final String childDbName = convertFromCamelCase(genericType);
743                                                 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
744                                                 EdgeRule rule;
745                                                 
746                                                 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
747                                                 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
748                                                         //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
749                                                         Direction ruleDirection = rule.getDirection();
750                                                         Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
751                                                         List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
752                                                         itr = verticesList.stream().filter(item -> {
753                                                                 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
754                                                         }).iterator();
755                                                         if (itr.hasNext()) {
756                                                                 getList = (List<Object>)obj.getValue(property);
757                                                         }
758                                                         int processed = 0;
759                                                         int removed = 0;
760                                                         while (itr.hasNext()) {
761                                                                 Vertex childVertex = itr.next();
762                                                                 if (!seen.contains(childVertex)) {
763                                                                         Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
764                                                                         
765                                                                         Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
766                                                                         if (result != null) {
767                                                                                 getList.add(argumentObject.getUnderlyingObject());
768                                                                         }
769                                                                         
770                                                                         processed++;
771                                                                 } else {
772                                                                         removed++;
773                                                                         LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
774                                                                 }
775                                                         }
776                                                         if (processed == 0) {
777                                                                 //vertices were all seen, reset the list
778                                                                 getList = null;
779                                                         }
780                                                         if (processed > 0) {
781                                                                 modified = true;
782                                                         }
783                                                 }
784                                         } else if (obj.isSimpleGenericType(property)) {
785                                                 List<Object> temp = this.engine.getListProperty(v, property);
786                                                 if (temp != null) {
787                                                         getList = (List<Object>)obj.getValue(property);
788                                                         getList.addAll(temp);
789                                                         modified = true;
790                                                 }
791
792                                         }
793
794                                 }
795
796                         }
797                 }
798                 
799                 //no changes were made to this obj, discard the instance
800                 if (!modified) {
801                         return null;
802                 }
803                 this.enrichData(obj, v);
804                 return obj;
805                 
806         }
807         
808         
809         public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
810                 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
811                 if (nodeType == null) {
812                         throw new AAIException("AAI_6143");
813                 }
814                 
815                 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
816                 Set<Vertex> seen = new HashSet<>();
817                 int depth = 0;
818                 String cleanUp = "false";
819                 boolean nodeOnly = true;
820                 StopWatch.conditionalStart();
821                 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
822                 dbTimeMsecs += StopWatch.stopIfStarted();
823                 return obj;
824                 
825         }
826         public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
827                 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
828                 if (nodeType == null) {
829                         throw new AAIException("AAI_6143");
830                 }
831                 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
832                 Set<Vertex> seen = new HashSet<>();
833                 int depth = AAIProperties.MAXIMUM_DEPTH;
834                 String cleanUp = "false";
835                 boolean nodeOnly = false;
836                 StopWatch.conditionalStart();
837                 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
838                 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
839                 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
840                 dbTimeMsecs += StopWatch.stopIfStarted();
841                 return obj;
842         }
843         /**
844          * Copy simple property.
845          *
846          * @param property the property
847          * @param obj the obj
848          * @param v the v
849          * @throws InstantiationException the instantiation exception
850          * @throws IllegalAccessException the illegal access exception
851          * @throws IllegalArgumentException the illegal argument exception
852          * @throws InvocationTargetException the invocation target exception
853          * @throws NoSuchMethodException the no such method exception
854          * @throws SecurityException the security exception
855          */
856         private void copySimpleProperty(String property, Introspector obj, Vertex v) {
857                 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
858                 String dbPropertyName = property;
859                 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
860                         dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
861                 }
862                 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
863                 if (temp != null) {
864                         
865                         obj.setValue(property, temp);
866                 }
867         }
868         
869         /**
870          * Simple db to object.
871          *
872          * @param obj the obj
873          * @param v the v
874          * @throws InstantiationException the instantiation exception
875          * @throws IllegalAccessException the illegal access exception
876          * @throws IllegalArgumentException the illegal argument exception
877          * @throws InvocationTargetException the invocation target exception
878          * @throws NoSuchMethodException the no such method exception
879          * @throws SecurityException the security exception
880          */
881         private void simpleDbToObject (Introspector obj, Vertex v) {
882                 for (String property : obj.getProperties()) {
883                         
884
885                         if (!(obj.isComplexType(property) || obj.isListType(property))) {
886                                 this.copySimpleProperty(property, obj, v);
887                         }
888                 }
889         }
890         
891         /**
892          * Creates the relationship list.
893          *
894          * @param v the v
895          * @param obj the obj
896          * @param cleanUp the clean up
897          * @return the object
898          * @throws InstantiationException the instantiation exception
899          * @throws IllegalAccessException the illegal access exception
900          * @throws IllegalArgumentException the illegal argument exception
901          * @throws InvocationTargetException the invocation target exception
902          * @throws NoSuchMethodException the no such method exception
903          * @throws SecurityException the security exception
904          * @throws UnsupportedEncodingException the unsupported encoding exception
905          * @throws AAIException the AAI exception
906          * @throws MalformedURLException the malformed URL exception
907          * @throws URISyntaxException 
908          */
909         private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
910                 
911                 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
912
913                 List<Object> relationshipObjList = obj.getValue("relationship");
914                 
915                 for (Vertex cousin : cousins) {
916                         if (VersionChecker.apiVersionNeedsEdgeLabel(obj.getVersion())) {
917                                 List<Edge> edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin);
918                                 for (Edge e : edges) {
919                                         Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
920                                         Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e);
921                                         if (result != null) {
922                                                 relationshipObjList.add(result);
923                                         }
924                                 }
925                         } else {
926                                 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
927                                 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
928                                 if (result != null) {
929                                         relationshipObjList.add(result);
930                                 }
931                         }
932
933                 }
934                 
935                 if (relationshipObjList.isEmpty()) {
936                         return null;
937                 } else {
938                         return obj;
939                 }
940         }
941         
942         /**
943          * Process edge relationship.
944          *
945          * @param relationshipObj the relationship obj
946          * @param edge the edge
947          * @param direction the direction
948          * @param cleanUp the clean up
949          * @return the object
950          * @throws InstantiationException the instantiation exception
951          * @throws IllegalAccessException the illegal access exception
952          * @throws IllegalArgumentException the illegal argument exception
953          * @throws InvocationTargetException the invocation target exception
954          * @throws NoSuchMethodException the no such method exception
955          * @throws SecurityException the security exception
956          * @throws UnsupportedEncodingException the unsupported encoding exception
957          * @throws AAIException the AAI exception
958          * @throws MalformedURLException the malformed URL exception
959          * @throws AAIUnknownObjectException 
960          * @throws URISyntaxException 
961          */
962         private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException {
963
964
965                 //we must look up all parents in this case because we need to compute name-properties
966                 //we cannot used the cached aaiUri to perform this action currently
967                 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
968                 //damaged vertex found, ignore
969                 if (!tuple.isPresent()) {
970                         return null;
971                 }
972                 List<Introspector> list = tuple.get().getValue1();
973                 URI uri = this.getURIFromList(list);
974                 
975                 URIToRelationshipObject uriParser = null;
976                 Introspector result = null;
977                 try {
978                         uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
979                         result = uriParser.getResult();
980                 } catch (AAIException | URISyntaxException e) {
981                         LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " 
982                                         + e.getMessage() + " " + LogFormatTools.getStackTop(e));
983                         if ("true".equals(cleanUp)) {
984                                 this.deleteWithTraversal(tuple.get().getValue0());
985                         }
986                         return null;
987                 }
988                 if (!list.isEmpty()) {
989                         this.addRelatedToProperty(result, list.get(0));
990                 }
991                 
992                 if (edge != null && result.hasProperty("relationship-label")) {
993                         result.setValue("relationship-label", edge.label());
994                 }
995                 
996                 return result.getUnderlyingObject();
997         }
998         
999         /**
1000          * Gets the URI for vertex.
1001          *
1002          * @param v the v
1003          * @return the URI for vertex
1004          * @throws InstantiationException the instantiation exception
1005          * @throws IllegalAccessException the illegal access exception
1006          * @throws IllegalArgumentException the illegal argument exception
1007          * @throws InvocationTargetException the invocation target exception
1008          * @throws NoSuchMethodException the no such method exception
1009          * @throws SecurityException the security exception
1010          * @throws UnsupportedEncodingException the unsupported encoding exception
1011          * @throws AAIUnknownObjectException 
1012          */
1013         public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1014                 
1015                 return getURIForVertex(v, false); 
1016         }
1017         
1018         public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
1019                 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1020                 
1021                 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1022                 
1023                 if (aaiUri != null && !overwrite) {
1024                         uri = UriBuilder.fromPath(aaiUri).build();
1025                 } else {
1026                         StopWatch.conditionalStart();
1027                         Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false);
1028                         dbTimeMsecs += StopWatch.stopIfStarted();
1029                         if (tuple.isPresent()) {
1030                                 List<Introspector> list = tuple.get().getValue1();
1031                                 uri = this.getURIFromList(list);
1032                         }
1033                         
1034                         
1035                 }
1036                 return uri;
1037         }
1038         /**
1039          * Gets the URI from list.
1040          *
1041          * @param list the list
1042          * @return the URI from list
1043          * @throws UnsupportedEncodingException the unsupported encoding exception
1044          */
1045         private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1046                 String uri = "";
1047                 StringBuilder sb = new StringBuilder();
1048                 for (Introspector i : list) {
1049                         sb.insert(0, i.getURI());
1050                 }
1051                 
1052                 uri = sb.toString();
1053                 return UriBuilder.fromPath(uri).build();
1054         }
1055         
1056         /**
1057          * Gets the parents.
1058          *
1059          * @param start the start
1060          * @param removeDamaged the remove damaged
1061          * @return the parents
1062          * @throws InstantiationException the instantiation exception
1063          * @throws IllegalAccessException the illegal access exception
1064          * @throws IllegalArgumentException the illegal argument exception
1065          * @throws InvocationTargetException the invocation target exception
1066          * @throws NoSuchMethodException the no such method exception
1067          * @throws SecurityException the security exception
1068          * @throws AAIUnknownObjectException 
1069          */
1070         private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1071                 
1072                 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1073                 List<Introspector> objs = new ArrayList<>();
1074                 boolean shortCircuit = false;
1075                 for (Vertex v : results) {
1076                         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1077                         Introspector obj = null;
1078                         //vertex on the other end of this edge is bad
1079                         if (nodeType == null) {
1080                                 //log something here about what was found and that it was removed
1081                                 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1082                                 if (removeDamaged) {
1083                                         this.deleteWithTraversal(v);
1084                                 }
1085                                 shortCircuit = true;
1086                         } else {
1087                                 try {
1088                                         obj = loader.introspectorFromName(nodeType);
1089                                 } catch (AAIUnknownObjectException e) {
1090                                         LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1091                                         obj = null;
1092                                 }
1093                         }
1094                         
1095                         if (obj == null) {
1096                                 //can't make a valid path because we don't understand this object
1097                                 // don't include it
1098                         } else {
1099                                 this.simpleDbToObject(obj, v);
1100                                 objs.add(obj);
1101                         }
1102                 }
1103                 
1104                 //stop processing and don't return anything for this bad vertex
1105                 if (shortCircuit) {
1106                         return Optional.empty();
1107                 }
1108                 
1109                 return Optional.of(new Pair<>(results.get(results.size()-1), objs));
1110         }
1111         /**
1112          * Takes a list of vertices and a list of objs and assumes they are in
1113          * the order you want the URIs to be nested.
1114          * [A,B,C] creates uris [A, AB, ABC]
1115          * @param vertices
1116          * @param objs
1117          * @throws UnsupportedEncodingException
1118          * @throws URISyntaxException
1119          */
1120         public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1121                 
1122                 StringBuilder uriChain = new StringBuilder();
1123                 for (int i = 0; i < vertices.size(); i++) {
1124                         String aaiUri = "";
1125                         Vertex v = null;
1126                         v = vertices.get(i);
1127                         aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1128                         if (aaiUri != null) {
1129                                 uriChain.append(aaiUri);
1130                         } else {
1131                                 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1132                                 aaiUri = uri.toString();
1133                                 uriChain.append(aaiUri);
1134                                 v.property(AAIProperties.AAI_URI, uriChain.toString());
1135                         }
1136                 }
1137                 
1138                 
1139                 
1140         }
1141         
1142         
1143         /**
1144          * Adds the r
1145          * @throws AAIUnknownObjectException 
1146          * @throws IllegalArgumentException elated to property.
1147          *
1148          * @param relationship the relationship
1149          * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1150          */
1151         public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1152                 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1153                 List<Introspector> relatedToProperties = new ArrayList<>();
1154                 
1155                 if (nameProps != null) {
1156                         String[] props = nameProps.split(",");
1157                         for (String prop : props) {
1158                                 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1159                                 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1160                                 relatedTo.setValue("property-value", child.getValue(prop));
1161                                 relatedToProperties.add(relatedTo);
1162                         }
1163                 }
1164                 
1165                 if (!relatedToProperties.isEmpty()) {
1166                         List relatedToList = (List)relationship.getValue("related-to-property");
1167                         for (Introspector obj : relatedToProperties) {
1168                                 relatedToList.add(obj.getUnderlyingObject());
1169                         }
1170                 }
1171                 
1172         }
1173         
1174         /**
1175          * Creates the edge.
1176          *
1177          * @param relationship the relationship
1178          * @param inputVertex the input vertex
1179          * @return true, if successful
1180          * @throws UnsupportedEncodingException the unsupported encoding exception
1181          * @throws AAIException the AAI exception
1182          */
1183         public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1184                 
1185                 Vertex relatedVertex = null;
1186                 StopWatch.conditionalStart();
1187                 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1188                 
1189                 String label = null;
1190                 if (relationship.hasProperty("relationship-label")) {
1191                         label = relationship.getValue("relationship-label");
1192                 }
1193                 
1194                 List<Vertex> results = parser.getQueryBuilder().toList();
1195                 if (results.isEmpty()) {
1196                         dbTimeMsecs += StopWatch.stopIfStarted();
1197                         AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1198                         e.getTemplateVars().add(parser.getResultType());
1199                         e.getTemplateVars().add(parser.getUri().toString());
1200                         throw e;
1201                 } else { 
1202                         //still an issue if there's more than one
1203                         relatedVertex = results.get(0);
1204                 }
1205
1206                 if (relatedVertex != null) {
1207
1208                         Edge e;
1209                         try {
1210                                 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1211                                 if (e == null) {
1212                                         edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1213                                 } else {
1214                                         //attempted to link two vertexes already linked
1215                                 }
1216                         } finally {
1217                                 dbTimeMsecs += StopWatch.stopIfStarted();
1218                         }
1219                 }
1220                 
1221                 dbTimeMsecs += StopWatch.stopIfStarted();
1222                 return true;
1223         }
1224         
1225         /**
1226          * Gets all the edges between of the type.
1227          *
1228          * @param aVertex the out vertex
1229          * @param bVertex the in vertex
1230          * @return the edges between
1231          * @throws AAIException the AAI exception
1232          * @throws NoEdgeRuleFoundException 
1233          */
1234         private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1235                 
1236                 List<Edge> result = new ArrayList<>();
1237                 
1238                 if (bVertex != null) {
1239                         GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1240                         findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1241                         if (EdgeType.TREE.equals(type)) {
1242                                 findEdgesBetween = findEdgesBetween.not(__.has(EdgeProperty.CONTAINS.toString(), "NONE"));
1243                         } else {
1244                                 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE");
1245                         }
1246                         findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1247                         result = findEdgesBetween.toList();
1248                 }
1249                 
1250                 return result;
1251         }
1252         /**
1253          * Gets all the edges between the vertexes with the label and type.
1254          *
1255          * @param aVertex the out vertex
1256          * @param bVertex the in vertex
1257          * @param label 
1258          * @return the edges between
1259          * @throws AAIException the AAI exception
1260          */
1261         private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1262                 
1263                 List<Edge> result = new ArrayList<>();
1264                 
1265                 if (bVertex != null) {
1266                         EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex, label);
1267                         List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1268                         for (Edge edge : edges) {
1269                                 if (edge.label().equals(rule.getLabel())) {
1270                                         result.add(edge);
1271                                 }
1272                         }               
1273                 }
1274                 
1275                 return result;
1276         }
1277         
1278         /**
1279          * Gets the edge between with the label and edge type.
1280          *
1281          * @param aVertex the out vertex
1282          * @param bVertex the in vertex
1283          * @param label 
1284          * @return the edge between
1285          * @throws AAIException the AAI exception
1286          * @throws NoEdgeRuleFoundException 
1287          */
1288         public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1289         
1290                 StopWatch.conditionalStart();
1291                 if (bVertex != null) {
1292
1293                                 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex, label);
1294                                 
1295                                 if (!edges.isEmpty()) {
1296                                         dbTimeMsecs += StopWatch.stopIfStarted();
1297                                         return edges.get(0);
1298                                 }
1299
1300                 }
1301                 dbTimeMsecs += StopWatch.stopIfStarted();
1302                 return null;
1303         }
1304         public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1305                 return this.getEdgeBetween(type, aVertex, bVertex, null);
1306         }
1307         
1308
1309         /**
1310          * Delete edge.
1311          *
1312          * @param relationship the relationship
1313          * @param inputVertex the input vertex
1314          * @return true, if successful
1315          * @throws UnsupportedEncodingException the unsupported encoding exception
1316          * @throws AAIException the AAI exception
1317          */
1318         public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1319                 
1320                 Vertex relatedVertex = null;
1321                 StopWatch.conditionalStart();
1322                 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1323                 
1324                 List<Vertex> results = parser.getQueryBuilder().toList();
1325                 
1326                 String label = null;
1327                 if (relationship.hasProperty("relationship-label")) {
1328                         label = relationship.getValue("relationship-label");
1329                 }
1330
1331                 if (results.isEmpty()) {
1332                         dbTimeMsecs += StopWatch.stopIfStarted();
1333                         return false;
1334                 }
1335                 
1336                 relatedVertex = results.get(0);
1337                 Edge edge;
1338                 try {
1339                         edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1340                 } catch (NoEdgeRuleFoundException e) {
1341                         dbTimeMsecs += StopWatch.stopIfStarted();
1342                         throw new AAIException("AAI_6129", e);
1343                 }
1344                 if (edge != null) {
1345                         edge.remove();
1346                         dbTimeMsecs += StopWatch.stopIfStarted();
1347                         return true;
1348                 } else {
1349                         dbTimeMsecs += StopWatch.stopIfStarted();
1350                         return false;
1351                 }
1352                 
1353         }
1354         
1355         /**
1356          * Delete items with traversal.
1357          *
1358          * @param vertexes the vertexes
1359          * @throws IllegalStateException the illegal state exception
1360          */
1361         public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1362                 
1363                 for (Vertex v : vertexes) {
1364             LOGGER.debug("About to delete the vertex with id: " + v.id());
1365                         deleteWithTraversal(v);
1366                 }
1367                 
1368         }
1369         
1370         /**
1371          * Delete with traversal.
1372          *
1373          * @param startVertex the start vertex
1374          */
1375         public void deleteWithTraversal(Vertex startVertex) {
1376                 StopWatch.conditionalStart();
1377                 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1378                 
1379                 for (Vertex v : results) {
1380                         LOGGER.warn("Removing vertex " + v.id().toString());
1381
1382                         v.remove();
1383                 }
1384                 dbTimeMsecs += StopWatch.stopIfStarted();
1385         }
1386
1387         /**
1388          * Delete.
1389          *
1390          * @param v the v
1391          * @param resourceVersion the resource version
1392          * @throws IllegalArgumentException the illegal argument exception
1393          * @throws AAIException the AAI exception
1394          * @throws InterruptedException the interrupted exception
1395          */
1396         public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1397         
1398                 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1399                 if (result) {
1400                         try {
1401                                 deleteWithTraversal(v);
1402                         } catch (IllegalStateException e) {
1403                                 throw new AAIException("AAI_6110", e);
1404                         }
1405
1406                 }
1407                 
1408         }
1409         
1410
1411         /**
1412          * Verify delete semantics.
1413          *
1414          * @param vertex the vertex
1415          * @param resourceVersion the resource version
1416          * @return true, if successful
1417          * @throws AAIException the AAI exception
1418          */
1419         private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1420                 boolean result = true;
1421                 String nodeType = "";
1422                 String errorDetail = " unknown delete semantic found";
1423                 String aaiExceptionCode = "";
1424                 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1425                 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1426                 }
1427                 StopWatch.conditionalStart();
1428                 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertex).union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)).dedup().toList();
1429                 
1430                 dbTimeMsecs += StopWatch.stopIfStarted();
1431                 if (!preventDeleteVertices.isEmpty()) {
1432                         aaiExceptionCode = "AAI_6110";
1433                         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);
1434                         result = false;
1435                 }
1436                 if (!result) {
1437                         throw new AAIException(aaiExceptionCode, errorDetail); 
1438                 }
1439                 return result;
1440         }
1441
1442         /**
1443          * Verify resource version.
1444          *
1445          * @param action the action
1446          * @param nodeType the node type
1447          * @param currentResourceVersion the current resource version
1448          * @param resourceVersion the resource version
1449          * @param uri the uri
1450          * @return true, if successful
1451          * @throws AAIException the AAI exception
1452          */
1453         public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1454                 String enabled = "";
1455                 String errorDetail = "";
1456                 String aaiExceptionCode = "";
1457                 if (currentResourceVersion == null) {
1458                         currentResourceVersion = "";
1459                 }
1460                 
1461                 if (resourceVersion == null) {
1462                         resourceVersion = "";
1463                 }
1464                 try {
1465                         enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1466                 } catch (AAIException e) {
1467                         ErrorLogHelper.logException(e);
1468                 }
1469                 // We're only doing the resource version checks for v5 and later
1470                 if (enabled.equals("true")) {
1471                         if (!currentResourceVersion.equals(resourceVersion)) {
1472                                 if (action.equals("create") && !resourceVersion.equals("")) {
1473                                         errorDetail = "resource-version passed for " + action + " of " + uri;
1474                                         aaiExceptionCode = "AAI_6135";
1475                                 } else if (resourceVersion.equals("")) {
1476                                         errorDetail = "resource-version not passed for " + action + " of " + uri;
1477                                         aaiExceptionCode = "AAI_6130";
1478                                 } else {
1479                                         errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1480                                         aaiExceptionCode = "AAI_6131";
1481                                 }
1482                                 
1483                                 throw new AAIException(aaiExceptionCode, errorDetail); 
1484                                 
1485                         }
1486                 }
1487                 return true;
1488         }
1489         
1490         /**
1491          * Convert from camel case.
1492          *
1493          * @param name the name
1494          * @return the string
1495          */
1496         private String convertFromCamelCase (String name) {
1497                 String result = "";
1498                 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1499                 
1500                 NamingExceptions exceptions = NamingExceptions.getInstance();
1501                 result = exceptions.getDBName(result);
1502                 
1503                 return result;
1504         }
1505         
1506         private boolean canModify(Introspector obj, String propName, String requestContext) {
1507                 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1508                 if (readOnly != null) {
1509                         final String[] items = readOnly.split(",");
1510                         for (String item : items) {
1511                                 if (requestContext.equals(item)) {
1512                                         return false;
1513                                 }
1514                         }
1515                 }
1516                 return true;
1517         }
1518         
1519         private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1520                 
1521                 SideEffectRunner runner = new SideEffectRunner
1522                                 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1523                 
1524                 runner.execute(obj, self);
1525         }
1526         
1527         private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1528                 
1529                 SideEffectRunner runner = new SideEffectRunner
1530                                 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1531                 
1532                 runner.execute(obj, self);
1533         }
1534         
1535         private void enrichData(Introspector obj, Vertex self) throws AAIException  {
1536                 
1537                 SideEffectRunner runner = new SideEffectRunner
1538                                 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1539                 
1540                 runner.execute(obj, self);
1541         }
1542
1543         public double getDBTimeMsecs() {
1544                 return (dbTimeMsecs);
1545         }
1546         
1547         /**
1548           * Db to object With Filters 
1549           * This is for a one-time run with Tenant Isloation to only filter relationships 
1550           * TODO: Chnage the original dbToObject to take filter parent/cousins 
1551           *
1552           * @param vertices the vertices
1553           * @param obj the obj
1554           * @param depth the depth
1555           * @param cleanUp the clean up
1556           * @return the introspector
1557           * @throws AAIException the AAI exception
1558           * @throws IllegalAccessException the illegal access exception
1559           * @throws IllegalArgumentException the illegal argument exception
1560           * @throws InvocationTargetException the invocation target exception
1561           * @throws SecurityException the security exception
1562           * @throws InstantiationException the instantiation exception
1563           * @throws NoSuchMethodException the no such method exception
1564           * @throws UnsupportedEncodingException the unsupported encoding exception
1565           * @throws MalformedURLException the malformed URL exception
1566          * @throws AAIUnknownObjectException 
1567          * @throws URISyntaxException 
1568           */
1569          //TODO - See if you can merge the 2 dbToObjectWithFilters
1570          public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,  List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException {
1571                         String cleanUp = "false";
1572                         if (depth < 0) {
1573                                 return null;
1574                         }
1575                         depth--;
1576                         seen.add(v);
1577                         boolean modified = false;
1578                         for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1579                                 List<Object> getList = null;
1580                                 Vertex[] vertices = null;
1581
1582                                 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1583                                         this.copySimpleProperty(property, obj, v);
1584                                         modified = true;
1585                                 } else {
1586                                         if (obj.isComplexType(property)) {
1587                                         /* container case */
1588                 
1589                                                 if (!property.equals("relationship-list") && depth >= 0) {
1590                                                         Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1591                                                         Object result  = dbToObjectWithFilters(argumentObject, v, seen, depth+1, nodeOnly,  filterCousinNodes, filterParentNodes);
1592                                                         if (result != null) {
1593                                                                 obj.setValue(property, argumentObject.getUnderlyingObject());
1594                                                                 modified = true;
1595                                                         }
1596                                                 } else if (property.equals("relationship-list") && !nodeOnly){
1597                                                         /* relationships need to be handled correctly */
1598                                                         Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1599                                                         relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes);
1600                                                         if (relationshipList != null) {
1601                                                                 modified = true;
1602                                                                 obj.setValue(property, relationshipList.getUnderlyingObject());
1603                                                                 modified = true;
1604                                                         }
1605                                                         
1606                                                 }
1607                                         } else if (obj.isListType(property)) {
1608                                                 
1609                                                 if (property.equals("any")) {
1610                                                         continue;
1611                                                 }
1612                                                 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1613                                                 if (obj.isComplexGenericType(property) && depth >= 0) {
1614                                                         final String childDbName = convertFromCamelCase(genericType);
1615                                                         String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1616                                                         EdgeRule rule;
1617                                                         
1618                                                         boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains);
1619                                                         
1620                                                         
1621                                                         
1622                                                         rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
1623                                                         if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) {
1624                                                                 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
1625                                                                 Direction ruleDirection = rule.getDirection();
1626                                                                 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
1627                                                                 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
1628                                                                 itr = verticesList.stream().filter(item -> {
1629                                                                         return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
1630                                                                 }).iterator();
1631                                                                 if (itr.hasNext()) {
1632                                                                         getList = (List<Object>)obj.getValue(property);
1633                                                                 }
1634                                                                 int processed = 0;
1635                                                                 int removed = 0;
1636                                                                 while (itr.hasNext()) {
1637                                                                         Vertex childVertex = itr.next();
1638                                                                         if (!seen.contains(childVertex)) {
1639                                                                                 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1640                                                                                 
1641                                                                                 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes);
1642                                                                                 if (result != null) {
1643                                                                                         getList.add(argumentObject.getUnderlyingObject());
1644                                                                                 }
1645                                                                                 
1646                                                                                 processed++;
1647                                                                         } else {
1648                                                                                 removed++;
1649                                                                                 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
1650                                                                         }
1651                                                                 }
1652                                                                 if (processed == 0) {
1653                                                                         //vertices were all seen, reset the list
1654                                                                         getList = null;
1655                                                                 }
1656                                                                 if (processed > 0) {
1657                                                                         modified = true;
1658                                                                 }
1659                                                         }
1660                                                 } else if (obj.isSimpleGenericType(property)) {
1661                                                         List<Object> temp = this.engine.getListProperty(v, property);
1662                                                         if (temp != null) {
1663                                                                 getList = (List<Object>)obj.getValue(property);
1664                                                                 getList.addAll(temp);
1665                                                                 modified = true;
1666                                                         }
1667
1668                                                 }
1669
1670                                         }
1671
1672                                 }
1673                         }
1674                         
1675                         //no changes were made to this obj, discard the instance
1676                         if (!modified) {
1677                                 return null;
1678                         }
1679                         this.enrichData(obj, v);
1680                         return obj;
1681                         
1682                 }
1683          
1684          /**
1685                  * Creates the relationship list with the filtered node types.
1686                  *
1687                  * @param v the v
1688                  * @param obj the obj
1689                  * @param cleanUp the clean up
1690                  * @return the object
1691                  * @throws InstantiationException the instantiation exception
1692                  * @throws IllegalAccessException the illegal access exception
1693                  * @throws IllegalArgumentException the illegal argument exception
1694                  * @throws InvocationTargetException the invocation target exception
1695                  * @throws NoSuchMethodException the no such method exception
1696                  * @throws SecurityException the security exception
1697                  * @throws UnsupportedEncodingException the unsupported encoding exception
1698                  * @throws AAIException the AAI exception
1699                  * @throws MalformedURLException the malformed URL exception
1700                  * @throws URISyntaxException 
1701                  */
1702                 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException {
1703                         List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
1704                         
1705                         Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
1706                                 String node = (String)item.property(AAIProperties.NODE_TYPE).orElse("");
1707                                 return filterNodes.parallelStream().anyMatch(node::contains);
1708                         }).iterator();
1709                         
1710                         
1711                         List<Vertex> cousins = (List<Vertex>)IteratorUtils.toList(cousinVertices);
1712                         
1713                         //items.parallelStream().anyMatch(inputStr::contains)
1714                         List<Object> relationshipObjList = obj.getValue("relationship");
1715                         for (Vertex cousin : cousins) {
1716                                 
1717                                         Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1718                                         Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1719                                         if (result != null) {
1720                                                 relationshipObjList.add(result);
1721                                         }
1722                                 
1723
1724                         }
1725                         
1726                         if (relationshipObjList.isEmpty()) {
1727                                 return null;
1728                         } else {
1729                                 return obj;
1730                         }
1731                 }
1732
1733 }