2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.aai.serialization.db;
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.google.common.base.CaseFormat;
26 import com.google.common.collect.Multimap;
27 import org.apache.commons.collections.IteratorUtils;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
30 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
31 import org.apache.tinkerpop.gremlin.structure.*;
32 import org.janusgraph.core.SchemaViolationException;
33 import org.javatuples.Triplet;
34 import org.onap.aai.concurrent.AaiCallable;
35 import org.onap.aai.config.SpringContextAware;
36 import org.onap.aai.db.props.AAIProperties;
37 import org.onap.aai.edges.EdgeIngestor;
38 import org.onap.aai.edges.EdgeRule;
39 import org.onap.aai.edges.EdgeRuleQuery;
40 import org.onap.aai.edges.TypeAlphabetizer;
41 import org.onap.aai.edges.enums.AAIDirection;
42 import org.onap.aai.edges.enums.EdgeField;
43 import org.onap.aai.edges.enums.EdgeProperty;
44 import org.onap.aai.edges.enums.EdgeType;
45 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
46 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.introspection.*;
49 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
50 import org.onap.aai.introspection.sideeffect.*;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.logging.LogFormatTools;
53 import org.onap.aai.logging.StopWatch;
54 import org.onap.aai.parsers.query.QueryParser;
55 import org.onap.aai.parsers.uri.URIParser;
56 import org.onap.aai.parsers.uri.URIToRelationshipObject;
57 import org.onap.aai.query.builder.QueryBuilder;
58 import org.onap.aai.schema.enums.ObjectMetadata;
59 import org.onap.aai.schema.enums.PropertyMetadata;
60 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
61 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
62 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
63 import org.onap.aai.serialization.engines.query.QueryEngine;
64 import org.onap.aai.serialization.tinkerpop.TreeBackedVertex;
65 import org.onap.aai.setup.SchemaVersion;
66 import org.onap.aai.setup.SchemaVersions;
67 import org.onap.aai.util.AAIConfig;
68 import org.onap.aai.util.AAIConstants;
69 import org.onap.aai.workarounds.NamingExceptions;
70 import org.springframework.context.ApplicationContext;
72 import javax.ws.rs.core.UriBuilder;
73 import java.io.UnsupportedEncodingException;
74 import java.lang.reflect.Array;
75 import java.lang.reflect.InvocationTargetException;
76 import java.net.MalformedURLException;
78 import java.net.URISyntaxException;
80 import java.util.concurrent.ExecutionException;
81 import java.util.concurrent.ExecutorService;
82 import java.util.concurrent.Future;
83 import java.util.function.Function;
84 import java.util.regex.Matcher;
85 import java.util.regex.Pattern;
86 import java.util.stream.Collectors;
88 public class DBSerializer {
90 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
92 private final TransactionalGraphEngine engine;
93 private final String sourceOfTruth;
94 private final ModelType introspectionType;
95 private final SchemaVersion version;
96 private final Loader latestLoader;
97 private EdgeSerializer edgeSer;
98 private EdgeIngestor edgeRules;
99 private final Loader loader;
100 private final String baseURL;
101 private double dbTimeMsecs = 0;
102 private long currentTimeMillis;
104 private SchemaVersions schemaVersions;
105 private Set<String> namedPropNodes;
107 * Instantiates a new DB serializer.
109 * @param version the version
110 * @param engine the engine
111 * @param introspectionType the introspection type
112 * @param sourceOfTruth the source of truth
113 * @throws AAIException
115 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
116 this.engine = engine;
117 this.sourceOfTruth = sourceOfTruth;
118 this.introspectionType = introspectionType;
119 this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
120 SchemaVersion LATEST = schemaVersions.getDefaultVersion();
121 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST);
122 this.version = version;
123 this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
124 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
125 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
126 this.currentTimeMillis = System.currentTimeMillis();
130 private void initBeans() {
131 //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
132 ApplicationContext ctx = SpringContextAware.getApplicationContext();
133 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
135 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
136 setEdgeSerializer(es);
139 private void backupESInit() {
140 setEdgeSerializer(new EdgeSerializer(this.edgeRules));
143 public void setEdgeSerializer(EdgeSerializer edgeSer) {
144 this.edgeSer = edgeSer;
147 public EdgeSerializer getEdgeSeriailizer() {
151 public void setEdgeIngestor(EdgeIngestor ei) {
155 public EdgeIngestor getEdgeIngestor() {
156 return this.edgeRules;
160 * Touch standard vertex properties.
163 * @param isNewVertex the is new vertex
165 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
166 String timeNowInSec = Long.toString(currentTimeMillis);
169 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
170 v.property(AAIProperties.CREATED_TS, timeNowInSec);
171 v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString());
173 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
174 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
175 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
179 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
181 v.property(AAIProperties.NODE_TYPE, nodeType);
182 touchStandardVertexProperties(v, isNewVertex);
188 * Creates the new vertex.
190 * @param wrappedObject the wrapped object
192 * @throws UnsupportedEncodingException the unsupported encoding exception
193 * @throws AAIException the AAI exception
195 public Vertex createNewVertex(Introspector wrappedObject) {
198 StopWatch.conditionalStart();
199 v = this.engine.tx().addVertex();
200 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
202 dbTimeMsecs += StopWatch.stopIfStarted();
210 * @param className the class name
214 * Removes the classpath from a class name
216 public String trimClassName(String className) {
217 String returnValue = "";
219 if (className.lastIndexOf('.') == -1) {
222 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
232 * @param uriQuery the uri query
233 * @param identifier the identifier
234 * @throws SecurityException the security exception
235 * @throws IllegalAccessException the illegal access exception
236 * @throws IllegalArgumentException the illegal argument exception
237 * @throws InvocationTargetException the invocation target exception
238 * @throws InstantiationException the instantiation exception
239 * @throws InterruptedException the interrupted exception
240 * @throws NoSuchMethodException the no such method exception
241 * @throws AAIException the AAI exception
242 * @throws UnsupportedEncodingException the unsupported encoding exception
243 * @throws AAIUnknownObjectException
245 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
246 StopWatch.conditionalStart();
248 if (uriQuery.isDependent()) {
249 //try to find the parent
250 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
251 if (!vertices.isEmpty()) {
252 Vertex parent = vertices.get(0);
253 this.reflectDependentVertex(parent, v, obj, requestContext);
255 dbTimeMsecs += StopWatch.stopIfStarted();
256 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
259 serializeSingleVertex(v, obj, requestContext);
262 } catch (SchemaViolationException e) {
263 dbTimeMsecs += StopWatch.stopIfStarted();
264 throw new AAIException("AAI_6117", e);
266 dbTimeMsecs += StopWatch.stopIfStarted();
269 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
270 StopWatch.conditionalStart();
272 boolean isTopLevel = obj.isTopLevel();
274 addUriIfNeeded(v, obj.getURI());
277 processObject(obj, v, requestContext);
279 URI uri = this.getURIForVertex(v);
280 URIParser parser = new URIParser(this.loader, uri);
281 if (parser.validate()) {
282 addUriIfNeeded(v, uri.toString());
285 } catch (SchemaViolationException e) {
286 throw new AAIException("AAI_6117", e);
288 dbTimeMsecs += StopWatch.stopIfStarted();
292 private void addUriIfNeeded(Vertex v, String uri) {
293 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
294 if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) {
295 v.property(AAIProperties.AAI_URI, uri);
302 * @param <T> the generic type
306 * @throws IllegalAccessException the illegal access exception
307 * @throws IllegalArgumentException the illegal argument exception
308 * @throws InvocationTargetException the invocation target exception
309 * @throws InstantiationException the instantiation exception
310 * @throws NoSuchMethodException the no such method exception
311 * @throws SecurityException the security exception
312 * @throws AAIException the AAI exception
313 * @throws UnsupportedEncodingException the unsupported encoding exception
314 * @throws AAIUnknownObjectException
317 * Helper method for reflectToDb
318 * Handles all the property setting
320 private <T> List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
321 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
322 properties.remove(AAIProperties.RESOURCE_VERSION);
323 List<Vertex> dependentVertexes = new ArrayList<>();
324 List<Vertex> processedVertexes = new ArrayList<>();
325 boolean isComplexType = false;
326 boolean isListType = false;
327 if (!obj.isContainer()) {
328 this.touchStandardVertexProperties(obj.getDbName(), v, false);
330 this.executePreSideEffects(obj, v);
331 for (String property : properties) {
333 final String propertyType;
334 propertyType = obj.getType(property);
335 isComplexType = obj.isComplexType(property);
336 isListType = obj.isListType(property);
337 value = obj.getValue(property);
339 if (!(isComplexType || isListType)) {
340 boolean canModify = this.canModify(obj, property, requestContext);
343 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
344 String dbProperty = property;
345 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
346 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
348 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
349 //data linked properties are ephemeral
350 //they are populated dynamically on GETs
354 if (!value.equals(v.property(dbProperty).orElse(null))) {
355 if (propertyType.toLowerCase().contains(".long")) {
356 v.property(dbProperty, new Integer(((Long) value).toString()));
358 v.property(dbProperty, value);
362 v.property(dbProperty).remove();
365 } else if (isListType) {
366 List<Object> list = (List<Object>) value;
367 if (obj.isComplexGenericType(property)) {
369 for (Object o : list) {
370 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
371 child.setURIChain(obj.getURI());
372 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
377 engine.setListProperty(v, property, list);
380 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
381 if (value != null) { //effectively ignore complex properties not included in the object we're processing
382 if (value.getClass().isArray()) {
384 int length = Array.getLength(value);
385 for (int i = 0; i < length; i++) {
386 Object arrayElement = Array.get(value, i);
387 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
388 child.setURIChain(obj.getURI());
389 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
392 } else if (!property.equals("relationship-list")) {
394 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
395 if (introspector.isContainer()) {
396 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
397 introspector.setURIChain(obj.getURI());
399 processedVertexes.addAll(processObject(introspector, v, requestContext));
402 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
403 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
406 } else if (property.equals("relationship-list")) {
407 handleRelationships(obj, v);
412 this.writeThroughDefaults(v, obj);
413 /* handle those vertexes not touched */
414 for (Vertex toBeRemoved : processedVertexes) {
415 dependentVertexes.remove(toBeRemoved);
417 this.deleteItemsWithTraversal(dependentVertexes);
419 this.executePostSideEffects(obj, v);
420 return processedVertexes;
424 * Handle relationships.
427 * @param vertex the vertex
428 * @throws SecurityException the security exception
429 * @throws IllegalAccessException the illegal access exception
430 * @throws IllegalArgumentException the illegal argument exception
431 * @throws InvocationTargetException the invocation target exception
432 * @throws UnsupportedEncodingException the unsupported encoding exception
433 * @throws AAIException the AAI exception
436 * Handles the explicit relationships defined for an obj
438 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
441 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
442 processRelationshipList(wrappedRl, vertex);
449 * Process relationship list.
451 * @param wrapped the wrapped
453 * @throws UnsupportedEncodingException the unsupported encoding exception
454 * @throws AAIException the AAI exception
456 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
458 List<Object> relationships = (List<Object>) wrapped.getValue("relationship");
460 List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
461 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
463 for (Object relationship : relationships) {
465 Vertex cousinVertex = null;
467 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
468 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
470 if (wrappedRel.hasProperty("relationship-label")) {
471 label = wrappedRel.getValue("relationship-label");
474 List<Vertex> results = parser.getQueryBuilder().toList();
475 if (results.isEmpty()) {
476 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
477 ex.getTemplateVars().add(parser.getResultType());
478 ex.getTemplateVars().add(parser.getUri().toString());
481 //still an issue if there's more than one
482 cousinVertex = results.get(0);
485 if (cousinVertex != null) {
486 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
487 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
488 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
491 if (!edgeRules.hasRule(baseQ.build())) {
492 throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
493 + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label) : "") + ".");
494 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
495 throw new AAIException("AAI_6145");
498 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
501 addEdges.add(new Triplet<>(v, cousinVertex, label));
503 existingEdges.remove(e);
508 for (Edge edge : existingEdges) {
511 for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
513 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
514 } catch (NoEdgeRuleFoundException e) {
515 throw new AAIException("AAI_6129", e);
522 * Write through defaults.
526 * @throws AAIUnknownObjectException
528 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
529 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
530 if (latest != null) {
531 Set<String> required = latest.getRequiredProperties();
533 for (String field : required) {
534 String defaultValue = null;
535 Object vertexProp = null;
536 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
537 if (defaultValue != null) {
538 vertexProp = v.<Object>property(field).orElse(null);
539 if (vertexProp == null) {
540 v.property(field, defaultValue);
550 * Reflect dependent vertex.
553 * @param dependentObj the dependent obj
555 * @throws IllegalAccessException the illegal access exception
556 * @throws IllegalArgumentException the illegal argument exception
557 * @throws InvocationTargetException the invocation target exception
558 * @throws InstantiationException the instantiation exception
559 * @throws NoSuchMethodException the no such method exception
560 * @throws SecurityException the security exception
561 * @throws AAIException the AAI exception
562 * @throws UnsupportedEncodingException the unsupported encoding exception
563 * @throws AAIUnknownObjectException
565 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
567 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
568 //List<Vertex> items = p.getQuery().toList();
569 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
570 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
571 query.createKeyQuery(dependentObj);
573 List<Vertex> items = query.toList();
575 Vertex dependentVertex = null;
576 if (items.size() == 1) {
577 dependentVertex = items.get(0);
578 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
580 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI());
581 dependentVertex = createNewVertex(dependentObj);
584 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
589 * Reflect dependent vertex.
591 * @param parent the parent
592 * @param child the child
595 * @throws IllegalAccessException the illegal access exception
596 * @throws IllegalArgumentException the illegal argument exception
597 * @throws InvocationTargetException the invocation target exception
598 * @throws InstantiationException the instantiation exception
599 * @throws NoSuchMethodException the no such method exception
600 * @throws SecurityException the security exception
601 * @throws AAIException the AAI exception
602 * @throws UnsupportedEncodingException the unsupported encoding exception
603 * @throws AAIUnknownObjectException
605 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
607 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
608 if (parentUri != null) {
611 addUriIfNeeded(child, parentUri + uri);
613 processObject(obj, child, requestContext);
616 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
619 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
620 if (canBeLinked != null && canBeLinked.equals("true")) {
621 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext));
622 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
624 child.property(AAIProperties.LINKED, true);
627 edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
633 private SchemaVersion getVerForContext(String requestContext) {
634 Pattern pattern = Pattern.compile("v[0-9]+");
635 Matcher m = pattern.matcher(requestContext);
639 return new SchemaVersion(requestContext);
646 * @param vertices the vertices
648 * @param depth the depth
649 * @param cleanUp the clean up
650 * @return the introspector
651 * @throws AAIException the AAI exception
652 * @throws IllegalAccessException the illegal access exception
653 * @throws IllegalArgumentException the illegal argument exception
654 * @throws InvocationTargetException the invocation target exception
655 * @throws SecurityException the security exception
656 * @throws InstantiationException the instantiation exception
657 * @throws NoSuchMethodException the no such method exception
658 * @throws UnsupportedEncodingException the unsupported encoding exception
659 * @throws MalformedURLException the malformed URL exception
660 * @throws AAIUnknownObjectException
661 * @throws URISyntaxException
663 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
664 final int internalDepth;
665 if (depth == Integer.MAX_VALUE) {
666 internalDepth = depth--;
668 internalDepth = depth;
670 StopWatch.conditionalStart();
671 if (vertices.size() > 1 && !obj.isContainer()) {
672 dbTimeMsecs += StopWatch.stopIfStarted();
673 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
674 } else if (obj.isContainer()) {
676 String listProperty = null;
677 for (String property : obj.getProperties()) {
678 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
679 listProperty = property;
683 final String propertyName = listProperty;
684 getList = (List) obj.getValue(listProperty);
686 /* This is an experimental multithreading experiment
689 ExecutorService pool = GetAllPool.getInstance().getPool();
691 List<Future<Object>> futures = new ArrayList<>();
693 QueryEngine tgEngine = this.engine.getQueryEngine();
694 for (Vertex v : vertices) {
696 AaiCallable<Object> task = new AaiCallable<Object>() {
698 public Object process() throws UnsupportedEncodingException, AAIException {
699 Set<Vertex> seen = new HashSet<>();
700 Introspector childObject;
702 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
703 } catch (AAIUnknownObjectException e) {
707 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp);
708 } catch (UnsupportedEncodingException e) {
710 } catch (AAIException e) {
713 return childObject.getUnderlyingObject();
714 //getList.add(childObject.getUnderlyingObject());
717 futures.add(pool.submit(task));
720 for (Future<Object> future : futures) {
722 getList.add(future.get());
723 } catch (ExecutionException e) {
724 dbTimeMsecs += StopWatch.stopIfStarted();
725 throw new AAIException("AAI_4000", e);
726 } catch (InterruptedException e) {
727 dbTimeMsecs += StopWatch.stopIfStarted();
728 throw new AAIException("AAI_4000", e);
731 } else if (vertices.size() == 1) {
732 Set<Vertex> seen = new HashSet<>();
733 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp);
738 dbTimeMsecs += StopWatch.stopIfStarted();
747 * @param seen the seen
748 * @param depth the depth
749 * @param cleanUp the clean up
750 * @return the introspector
751 * @throws IllegalAccessException the illegal access exception
752 * @throws IllegalArgumentException the illegal argument exception
753 * @throws InvocationTargetException the invocation target exception
754 * @throws SecurityException the security exception
755 * @throws InstantiationException the instantiation exception
756 * @throws NoSuchMethodException the no such method exception
757 * @throws UnsupportedEncodingException the unsupported encoding exception
758 * @throws AAIException the AAI exception
759 * @throws MalformedURLException the malformed URL exception
760 * @throws AAIUnknownObjectException
761 * @throws URISyntaxException
763 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
771 boolean modified = false;
772 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
773 List<Object> getList = null;
774 Vertex[] vertices = null;
776 if (!(obj.isComplexType(property) || obj.isListType(property))) {
777 this.copySimpleProperty(property, obj, v);
780 if (obj.isComplexType(property)) {
783 if (!property.equals("relationship-list") && depth >= 0) {
784 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
785 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
786 if (result != null) {
787 obj.setValue(property, argumentObject.getUnderlyingObject());
790 } else if (property.equals("relationship-list") && !nodeOnly) {
791 /* relationships need to be handled correctly */
792 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
793 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
794 if (relationshipList != null) {
796 obj.setValue(property, relationshipList.getUnderlyingObject());
801 } else if (obj.isListType(property)) {
803 if (property.equals("any")) {
806 String genericType = obj.getGenericTypeClass(property).getSimpleName();
807 if (obj.isComplexGenericType(property) && depth >= 0) {
808 final String childDbName = convertFromCamelCase(genericType);
809 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
813 rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
814 } catch (EdgeRuleNotFoundException e) {
815 throw new NoEdgeRuleFoundException(e);
816 } catch (AmbiguousRuleChoiceException e) {
817 throw new MultipleEdgeRuleFoundException(e);
819 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
820 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
821 Direction ruleDirection = rule.getDirection();
822 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
823 List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
824 itr = verticesList.stream().filter(item -> {
825 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
828 getList = (List<Object>) obj.getValue(property);
832 while (itr.hasNext()) {
833 Vertex childVertex = itr.next();
834 if (!seen.contains(childVertex)) {
835 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
837 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
838 if (result != null) {
839 getList.add(argumentObject.getUnderlyingObject());
845 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
848 if (processed == 0) {
849 //vertices were all seen, reset the list
856 } else if (obj.isSimpleGenericType(property)) {
857 List<Object> temp = this.engine.getListProperty(v, property);
859 getList = (List<Object>) obj.getValue(property);
860 getList.addAll(temp);
871 //no changes were made to this obj, discard the instance
875 this.enrichData(obj, v);
881 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
882 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
883 if (nodeType == null) {
884 throw new AAIException("AAI_6143");
887 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
888 Set<Vertex> seen = new HashSet<>();
890 String cleanUp = "false";
891 boolean nodeOnly = true;
892 StopWatch.conditionalStart();
893 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
894 dbTimeMsecs += StopWatch.stopIfStarted();
899 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
900 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
901 if (nodeType == null) {
902 throw new AAIException("AAI_6143");
904 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
905 Set<Vertex> seen = new HashSet<>();
906 int depth = AAIProperties.MAXIMUM_DEPTH;
907 String cleanUp = "false";
908 boolean nodeOnly = false;
909 StopWatch.conditionalStart();
910 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
911 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
912 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
913 dbTimeMsecs += StopWatch.stopIfStarted();
918 * Copy simple property.
920 * @param property the property
923 * @throws InstantiationException the instantiation exception
924 * @throws IllegalAccessException the illegal access exception
925 * @throws IllegalArgumentException the illegal argument exception
926 * @throws InvocationTargetException the invocation target exception
927 * @throws NoSuchMethodException the no such method exception
928 * @throws SecurityException the security exception
930 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
931 final Object temp = getProperty(obj, property, v);
933 obj.setValue(property, temp);
939 * Load the introspector from the hashmap for the given property key
941 * @param property - vertex property
942 * @param obj - introspector object representing the vertex
943 * @param hashMap - Containing a list of pre-fetched properties for a given vertex
945 private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap){
947 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
948 String dbPropertyName = property;
950 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
951 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
954 final Object temp = hashMap.getOrDefault(dbPropertyName, null);
957 obj.setValue(property, temp);
962 * Simple db to object.
966 * @throws InstantiationException the instantiation exception
967 * @throws IllegalAccessException the illegal access exception
968 * @throws IllegalArgumentException the illegal argument exception
969 * @throws InvocationTargetException the invocation target exception
970 * @throws NoSuchMethodException the no such method exception
971 * @throws SecurityException the security exception
973 private void simpleDbToObject(Introspector obj, Vertex v) {
974 for(String key : obj.getProperties()){
975 this.copySimpleProperty(key, obj, v);
980 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v){
982 long startTime = System.currentTimeMillis();
984 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
985 String[] simplePropsArray = new String[simpleProperties.size()];
986 simplePropsArray = simpleProperties.toArray(simplePropsArray);
988 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
990 v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value()));
992 return simplePropsHashMap;
995 public Introspector dbToRelationshipObject(Vertex v) throws UnsupportedEncodingException, AAIException {
996 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
997 relationshipList = createRelationshipList(v, relationshipList, "false");
998 return relationshipList;
1001 * Creates the relationship list.
1004 * @param obj the obj
1005 * @param cleanUp the clean up
1006 * @return the object
1007 * @throws InstantiationException the instantiation exception
1008 * @throws IllegalAccessException the illegal access exception
1009 * @throws IllegalArgumentException the illegal argument exception
1010 * @throws InvocationTargetException the invocation target exception
1011 * @throws NoSuchMethodException the no such method exception
1012 * @throws SecurityException the security exception
1013 * @throws UnsupportedEncodingException the unsupported encoding exception
1014 * @throws AAIException the AAI exception
1015 * @throws MalformedURLException the malformed URL exception
1016 * @throws URISyntaxException
1018 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
1020 String[] cousinRules = new String[0];
1023 cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName());
1024 } catch (ExecutionException e) {
1025 LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", obj.getDbName(), e);
1028 List<Vertex> cousins = null;
1029 if(cousinRules != null && cousinRules.length != 0){
1030 cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules);
1032 cousins = this.engine.getQueryEngine().findCousinVertices(v);
1035 List<Object> relationshipObjList = obj.getValue("relationship");
1036 String aNodeType = v.property("aai-node-type").value().toString();
1038 TypeAlphabetizer alphabetizer = new TypeAlphabetizer();
1040 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1041 Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys();
1043 // For the given vertex, find all the cousins
1044 // For each cousin retrieve the node type and then
1045 // check if the version is greater than the edge label version
1046 // meaning is the current version equal to greater than the version
1047 // where we introduced the edge labels into the relationship payload
1048 // If it is, then we check if the edge key there are multiple labels
1049 // If there are multiple labels, then we need to go to the database
1050 // to retrieve the labels between itself and cousin vertex
1051 // If there is only single label between the edge a and b, then
1052 // we can retrieve what that is without going to the database
1053 // from using the edge rules json and get the edge rule out of it
1054 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType);
1055 for (Vertex cousin : cousins) {
1056 VertexProperty vertexProperty = cousin.property("aai-node-type");
1057 String bNodeType = null;
1058 if(vertexProperty.isPresent()){
1059 bNodeType = cousin.property("aai-node-type").value().toString();
1061 // If the vertex is missing the aai-node-type
1062 // Then its either a bad vertex or its in the process
1063 // of getting deleted so we should ignore these vertexes
1064 if(LOGGER.isDebugEnabled()){
1065 LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString());
1067 LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log");
1071 if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) {
1072 String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType);
1073 if(keysWithMultipleLabels.contains(edgeKey)){
1074 List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin);
1075 for(String edgeLabel: edgeLabels){
1076 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1077 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel);
1078 if (result != null) {
1079 relationshipObjList.add(result);
1084 EdgeRule edgeRule = null;
1086 // Create a query based on the a nodetype and b nodetype
1087 // which is also a cousin edge and ensure the version
1088 // is used properly so for example in order to be backwards
1089 // compatible if we had allowed a edge between a and b
1090 // in a previous release and we decided to remove it from
1091 // the edge rules in the future we can display the edge
1092 // only for the older apis and the new apis if the edge rule
1093 // is removed will not be seen in the newer version of the API
1095 EdgeRuleQuery ruleQuery = queryBuilder
1097 .edgeType(EdgeType.COUSIN)
1098 .version(obj.getVersion())
1102 edgeRule = edgeIngestor.getRule(ruleQuery);
1103 } catch (EdgeRuleNotFoundException e) {
1104 LOGGER.warn("Caught an edge rule not found exception for query {}, {}," +
1105 " it could be the edge rule is no longer valid for the existing edge in db",
1106 ruleQuery, LogFormatTools.getStackTop(e));
1108 } catch (AmbiguousRuleChoiceException e) {
1109 LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}",
1110 ruleQuery, LogFormatTools.getStackTop(e));
1114 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1115 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp,edgeRule.getLabel());
1116 if (result != null) {
1117 relationshipObjList.add(result);
1121 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1122 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1123 if (result != null) {
1124 relationshipObjList.add(result);
1130 if (relationshipObjList.isEmpty()) {
1138 * Process edge relationship.
1140 * @param relationshipObj the relationship obj
1141 * @param edge the edge
1142 * @param cleanUp the clean up
1143 * @return the object
1144 * @throws InstantiationException the instantiation exception
1145 * @throws IllegalAccessException the illegal access exception
1146 * @throws IllegalArgumentException the illegal argument exception
1147 * @throws InvocationTargetException the invocation target exception
1148 * @throws NoSuchMethodException the no such method exception
1149 * @throws SecurityException the security exception
1150 * @throws UnsupportedEncodingException the unsupported encoding exception
1151 * @throws AAIException the AAI exception
1152 * @throws MalformedURLException the malformed URL exception
1153 * @throws AAIUnknownObjectException
1154 * @throws URISyntaxException
1156 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException {
1158 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1160 if(!aaiUriProperty.isPresent()){
1164 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1166 URIToRelationshipObject uriParser = null;
1167 Introspector result = null;
1169 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1170 result = uriParser.getResult();
1171 } catch (AAIException | URISyntaxException e) {
1172 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + ": "
1173 + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1177 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1179 if(cousinVertexNodeType.isPresent()){
1180 String cousinType = cousinVertexNodeType.value().toString();
1181 if(namedPropNodes.contains(cousinType)){
1182 this.addRelatedToProperty(result, cousin, cousinType);
1186 if (edgeLabel != null && result.hasProperty("relationship-label")) {
1187 result.setValue("relationship-label", edgeLabel);
1190 return result.getUnderlyingObject();
1194 * Gets the URI for vertex.
1197 * @return the URI for vertex
1198 * @throws InstantiationException the instantiation exception
1199 * @throws IllegalAccessException the illegal access exception
1200 * @throws IllegalArgumentException the illegal argument exception
1201 * @throws InvocationTargetException the invocation target exception
1202 * @throws NoSuchMethodException the no such method exception
1203 * @throws SecurityException the security exception
1204 * @throws UnsupportedEncodingException the unsupported encoding exception
1205 * @throws AAIUnknownObjectException
1207 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1209 return getURIForVertex(v, false);
1212 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
1213 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1215 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1217 if (aaiUri != null && !overwrite) {
1218 uri = UriBuilder.fromPath(aaiUri).build();
1225 * Gets the URI from list.
1227 * @param list the list
1228 * @return the URI from list
1229 * @throws UnsupportedEncodingException the unsupported encoding exception
1231 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1233 StringBuilder sb = new StringBuilder();
1234 for (Introspector i : list) {
1235 sb.insert(0, i.getURI());
1238 uri = sb.toString();
1239 return UriBuilder.fromPath(uri).build();
1242 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType) throws AAIUnknownObjectException {
1243 Introspector obj = this.latestLoader.introspectorFromName(cousinType);
1244 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1245 List<Introspector> relatedToProperties = new ArrayList<>();
1247 if (nameProps != null) {
1248 String[] props = nameProps.split(",");
1249 for (String prop : props) {
1250 final Object temp = getProperty(obj, prop, cousinVertex);
1251 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1252 relatedTo.setValue("property-key", cousinType + "." + prop);
1253 relatedTo.setValue("property-value", temp);
1254 relatedToProperties.add(relatedTo);
1258 if (!relatedToProperties.isEmpty()) {
1259 List relatedToList = (List) relationship.getValue("related-to-property");
1260 for (Introspector introspector : relatedToProperties) {
1261 relatedToList.add(introspector.getUnderlyingObject());
1267 private Object getProperty(Introspector obj, String prop, Vertex vertex){
1269 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1270 String dbPropertyName = prop;
1272 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1273 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1276 return vertex.<Object>property(dbPropertyName).orElse(null);
1282 * @param relationship the relationship
1283 * @param inputVertex the input vertex
1284 * @return true, if successful
1285 * @throws UnsupportedEncodingException the unsupported encoding exception
1286 * @throws AAIException the AAI exception
1288 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1290 Vertex relatedVertex = null;
1291 StopWatch.conditionalStart();
1292 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1294 String label = null;
1295 if (relationship.hasProperty("relationship-label")) {
1296 label = relationship.getValue("relationship-label");
1299 List<Vertex> results = parser.getQueryBuilder().toList();
1300 if (results.isEmpty()) {
1301 dbTimeMsecs += StopWatch.stopIfStarted();
1302 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1303 e.getTemplateVars().add(parser.getResultType());
1304 e.getTemplateVars().add(parser.getUri().toString());
1307 //still an issue if there's more than one
1308 relatedVertex = results.get(0);
1311 if (relatedVertex != null) {
1315 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1317 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1319 //attempted to link two vertexes already linked
1322 dbTimeMsecs += StopWatch.stopIfStarted();
1326 dbTimeMsecs += StopWatch.stopIfStarted();
1331 * Gets all the edges between of the type with the specified label.
1333 * @param aVertex the out vertex
1334 * @param bVertex the in vertex
1335 * @return the edges between
1336 * @throws AAIException the AAI exception
1337 * @throws NoEdgeRuleFoundException
1339 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1343 if (bVertex != null) {
1344 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1345 if (EdgeType.TREE.equals(type)) {
1346 GraphTraversal<Vertex,Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1347 if(edgeRule.getDirection().equals(Direction.IN)){
1348 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1349 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1351 __.has(EdgeField.PRIVATE.toString(), true)
1354 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1355 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1357 __.has(EdgeField.PRIVATE.toString(), true)
1360 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1362 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1363 findEdgesBetween = findEdgesBetween
1364 .has(EdgeProperty.CONTAINS.toString(), "NONE")
1366 __.has(EdgeField.PRIVATE.toString(), true)
1368 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1370 List<Edge> list = findEdgesBetween.toList();
1371 if(!list.isEmpty()){
1372 result = list.get(0);
1380 * Gets all the edges between of the type.
1382 * @param aVertex the out vertex
1383 * @param bVertex the in vertex
1384 * @return the edges between
1385 * @throws AAIException the AAI exception
1386 * @throws NoEdgeRuleFoundException
1388 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1390 List<Edge> result = new ArrayList<>();
1392 if (bVertex != null) {
1393 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1394 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1395 if (EdgeType.TREE.equals(type)) {
1396 findEdgesBetween = findEdgesBetween
1399 __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1400 __.has(EdgeField.PRIVATE.toString(), true)
1404 findEdgesBetween = findEdgesBetween
1405 .has(EdgeProperty.CONTAINS.toString(), "NONE")
1407 __.has(EdgeField.PRIVATE.toString(), true)
1410 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1411 result = findEdgesBetween.toList();
1418 * Gets all the edges string between of the type.
1420 * @param aVertex the out vertex
1421 * @param bVertex the in vertex
1422 * @return the edges between
1423 * @throws AAIException the AAI exception
1424 * @throws NoEdgeRuleFoundException
1426 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1428 List<String> result = new ArrayList<>();
1430 if (bVertex != null) {
1431 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1432 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1433 if (EdgeType.TREE.equals(type)) {
1434 findEdgesBetween = findEdgesBetween
1437 __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1438 __.has(EdgeField.PRIVATE.toString(), true)
1442 findEdgesBetween = findEdgesBetween
1443 .has(EdgeProperty.CONTAINS.toString(), "NONE")
1445 __.has(EdgeField.PRIVATE.toString(), true)
1448 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1449 result = findEdgesBetween.label().toList();
1455 * Gets all the edges string between of the type.
1457 * @param aVertex the out vertex
1458 * @param bVertex the in vertex
1459 * @return the edges between
1460 * @throws AAIException the AAI exception
1461 * @throws NoEdgeRuleFoundException
1463 private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) {
1467 if (bVertex != null) {
1468 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1469 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1470 findEdgesBetween = findEdgesBetween
1471 .has(EdgeProperty.CONTAINS.toString(), "NONE")
1473 __.has(EdgeField.PRIVATE.toString(), true)
1475 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1476 result = findEdgesBetween.count().next();
1481 * Gets all the edges between the vertexes with the label and type.
1483 * @param aVertex the out vertex
1484 * @param bVertex the in vertex
1486 * @return the edges between
1487 * @throws AAIException the AAI exception
1489 private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1493 if (bVertex != null) {
1494 String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1495 String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1496 EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1499 rule = edgeRules.getRule(q);
1500 } catch (EdgeRuleNotFoundException e) {
1501 throw new NoEdgeRuleFoundException(e);
1502 } catch (AmbiguousRuleChoiceException e) {
1503 throw new MultipleEdgeRuleFoundException(e);
1505 edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1512 * Gets the edge between with the label and edge type.
1514 * @param aVertex the out vertex
1515 * @param bVertex the in vertex
1517 * @return the edge between
1518 * @throws AAIException the AAI exception
1519 * @throws NoEdgeRuleFoundException
1521 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1523 StopWatch.conditionalStart();
1524 if (bVertex != null) {
1526 Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1528 dbTimeMsecs += StopWatch.stopIfStarted();
1533 dbTimeMsecs += StopWatch.stopIfStarted();
1537 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1538 return this.getEdgeBetween(type, aVertex, bVertex, null);
1545 * @param relationship the relationship
1546 * @param inputVertex the input vertex
1547 * @return true, if successful
1548 * @throws UnsupportedEncodingException the unsupported encoding exception
1549 * @throws AAIException the AAI exception
1551 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1553 Vertex relatedVertex = null;
1554 StopWatch.conditionalStart();
1555 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1557 List<Vertex> results = parser.getQueryBuilder().toList();
1559 String label = null;
1560 if (relationship.hasProperty("relationship-label")) {
1561 label = relationship.getValue("relationship-label");
1564 if (results.isEmpty()) {
1565 dbTimeMsecs += StopWatch.stopIfStarted();
1569 relatedVertex = results.get(0);
1572 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1573 } catch (NoEdgeRuleFoundException e) {
1574 dbTimeMsecs += StopWatch.stopIfStarted();
1575 throw new AAIException("AAI_6129", e);
1579 dbTimeMsecs += StopWatch.stopIfStarted();
1582 dbTimeMsecs += StopWatch.stopIfStarted();
1589 * Delete items with traversal.
1591 * @param vertexes the vertexes
1592 * @throws IllegalStateException the illegal state exception
1594 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1596 for (Vertex v : vertexes) {
1597 LOGGER.debug("About to delete the vertex with id: " + v.id());
1598 deleteWithTraversal(v);
1604 * Delete with traversal.
1606 * @param startVertex the start vertex
1608 public void deleteWithTraversal(Vertex startVertex) {
1609 StopWatch.conditionalStart();
1610 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1612 for (Vertex v : results) {
1613 LOGGER.warn("Removing vertex " + v.id().toString());
1617 dbTimeMsecs += StopWatch.stopIfStarted();
1624 * @param resourceVersion the resource version
1625 * @throws IllegalArgumentException the illegal argument exception
1626 * @throws AAIException the AAI exception
1627 * @throws InterruptedException the interrupted exception
1629 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1631 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1633 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1634 * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted
1635 * So its better to make these in 2 steps
1637 if (result && !deletableVertices.isEmpty()) {
1638 result = verifyPreventDeleteSemantics(deletableVertices);
1643 deleteWithTraversal(v);
1644 } catch (IllegalStateException e) {
1645 throw new AAIException("AAI_6110", e);
1657 * @param resourceVersion the resource version
1658 * @throws IllegalArgumentException the illegal argument exception
1659 * @throws AAIException the AAI exception
1660 * @throws InterruptedException the interrupted exception
1662 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1664 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1669 deleteWithTraversal(v);
1670 } catch (IllegalStateException e) {
1671 throw new AAIException("AAI_6110", e);
1679 * Verify delete semantics.
1681 * @param vertex the vertex
1682 * @param resourceVersion the resource version
1683 * @return true, if successful
1684 * @throws AAIException the AAI exception
1686 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1687 boolean result = true;
1688 String nodeType = "";
1689 String errorDetail = " unknown delete semantic found";
1690 String aaiExceptionCode = "";
1691 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1692 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1694 List<Vertex> vertices = new ArrayList<Vertex>();
1695 vertices.add(vertex);
1696 result = verifyPreventDeleteSemantics(vertices);
1702 * Verify Prevent delete semantics.
1704 * @param vertices the list of vertices
1705 * @return true, if successful
1706 * @throws AAIException the AAI exception
1708 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
1709 boolean result = true;
1710 String nodeType = "";
1711 String errorDetail = " unknown delete semantic found";
1712 String aaiExceptionCode = "";
1714 StopWatch.conditionalStart();
1716 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition
1717 * If yes - that should prevent the deletion of the vertex
1718 * Dedup makes sure we dont capture the prevent-delete vertices twice
1719 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
1722 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices).
1723 union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE),
1724 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE))
1727 dbTimeMsecs += StopWatch.stopIfStarted();
1728 if (!preventDeleteVertices.isEmpty()) {
1729 aaiExceptionCode = "AAI_6110";
1730 errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices);
1734 throw new AAIException(aaiExceptionCode, errorDetail);
1740 * Verify resource version.
1742 * @param action the action
1743 * @param nodeType the node type
1744 * @param currentResourceVersion the current resource version
1745 * @param resourceVersion the resource version
1746 * @param uri the uri
1747 * @return true, if successful
1748 * @throws AAIException the AAI exception
1750 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1751 String enabled = "";
1752 String errorDetail = "";
1753 String aaiExceptionCode = "";
1754 boolean isDeleteResourceVersionOk = true;
1755 if (currentResourceVersion == null) {
1756 currentResourceVersion = "";
1759 if (resourceVersion == null) {
1760 resourceVersion = "";
1763 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1765 } catch (AAIException e) {
1766 ErrorLogHelper.logException(e);
1768 if (enabled.equals("true")) {
1769 if ("delete".equals(action)) {
1770 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
1772 if ((!isDeleteResourceVersionOk) || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
1773 if ("create".equals(action) && !resourceVersion.equals("")) {
1774 errorDetail = "resource-version passed for " + action + " of " + uri;
1775 aaiExceptionCode = "AAI_6135";
1776 } else if (resourceVersion.equals("")) {
1777 errorDetail = "resource-version not passed for " + action + " of " + uri;
1778 aaiExceptionCode = "AAI_6130";
1780 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1781 aaiExceptionCode = "AAI_6131";
1784 throw new AAIException(aaiExceptionCode, errorDetail);
1792 * Verify resource version for delete.
1794 * @param currentResourceVersion the current resource version
1795 * @param resourceVersion the resource version
1796 * @return true, if successful or false if there is a mismatch
1798 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
1800 boolean isDeleteResourceVersionOk = true;
1801 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
1802 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
1804 if ((!currentResourceVersion.equals(resourceVersion)) && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
1805 isDeleteResourceVersionOk = false;
1807 return isDeleteResourceVersionOk;
1811 * Convert from camel case.
1813 * @param name the name
1814 * @return the string
1816 private String convertFromCamelCase(String name) {
1818 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1820 NamingExceptions exceptions = NamingExceptions.getInstance();
1821 result = exceptions.getDBName(result);
1826 private boolean canModify(Introspector obj, String propName, String requestContext) {
1827 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1828 if (readOnly != null) {
1829 final String[] items = readOnly.split(",");
1830 for (String item : items) {
1831 if (requestContext.equals(item)) {
1839 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1841 SideEffectRunner runner = new SideEffectRunner
1842 .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build();
1844 runner.execute(obj, self);
1847 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1849 SideEffectRunner runner = new SideEffectRunner
1850 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1852 runner.execute(obj, self);
1855 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1857 SideEffectRunner runner = new SideEffectRunner
1858 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1860 runner.execute(obj, self);
1863 public double getDBTimeMsecs() {
1864 return (dbTimeMsecs);
1868 * Db to object With Filters
1869 * This is for a one-time run with Tenant Isloation to only filter relationships
1870 * TODO: Chnage the original dbToObject to take filter parent/cousins
1872 * @param obj the obj
1873 * @param v the vertex from the graph
1874 * @param depth the depth
1875 * @param nodeOnly specify if to exclude relationships or not
1876 * @param filterCousinNodes
1877 * @return the introspector
1878 * @throws AAIException the AAI exception
1879 * @throws IllegalAccessException the illegal access exception
1880 * @throws IllegalArgumentException the illegal argument exception
1881 * @throws InvocationTargetException the invocation target exception
1882 * @throws SecurityException the security exception
1883 * @throws InstantiationException the instantiation exception
1884 * @throws NoSuchMethodException the no such method exception
1885 * @throws UnsupportedEncodingException the unsupported encoding exception
1886 * @throws MalformedURLException the malformed URL exception
1887 * @throws AAIUnknownObjectException
1888 * @throws URISyntaxException
1890 //TODO - See if you can merge the 2 dbToObjectWithFilters
1891 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException {
1892 String cleanUp = "false";
1898 boolean modified = false;
1899 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1900 List<Object> getList = null;
1901 Vertex[] vertices = null;
1903 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1904 this.copySimpleProperty(property, obj, v);
1907 if (obj.isComplexType(property)) {
1908 /* container case */
1910 if (!property.equals("relationship-list") && depth >= 0) {
1911 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1912 Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, filterCousinNodes, filterParentNodes);
1913 if (result != null) {
1914 obj.setValue(property, argumentObject.getUnderlyingObject());
1917 } else if (property.equals("relationship-list") && !nodeOnly) {
1918 /* relationships need to be handled correctly */
1919 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1920 relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes);
1921 if (relationshipList != null) {
1923 obj.setValue(property, relationshipList.getUnderlyingObject());
1928 } else if (obj.isListType(property)) {
1930 if (property.equals("any")) {
1933 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1934 if (obj.isComplexGenericType(property) && depth >= 0) {
1935 final String childDbName = convertFromCamelCase(genericType);
1936 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1939 boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains);
1941 EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
1944 rule = edgeRules.getRule(q);
1945 } catch (EdgeRuleNotFoundException e) {
1946 throw new NoEdgeRuleFoundException(e);
1947 } catch (AmbiguousRuleChoiceException e) {
1948 throw new MultipleEdgeRuleFoundException(e);
1950 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) {
1951 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
1952 Direction ruleDirection = rule.getDirection();
1953 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
1954 List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
1955 itr = verticesList.stream().filter(item -> {
1956 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
1958 if (itr.hasNext()) {
1959 getList = (List<Object>) obj.getValue(property);
1963 while (itr.hasNext()) {
1964 Vertex childVertex = itr.next();
1965 if (!seen.contains(childVertex)) {
1966 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1968 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes);
1969 if (result != null) {
1970 getList.add(argumentObject.getUnderlyingObject());
1976 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
1979 if (processed == 0) {
1980 //vertices were all seen, reset the list
1983 if (processed > 0) {
1987 } else if (obj.isSimpleGenericType(property)) {
1988 List<Object> temp = this.engine.getListProperty(v, property);
1990 getList = (List<Object>) obj.getValue(property);
1991 getList.addAll(temp);
2002 //no changes were made to this obj, discard the instance
2006 this.enrichData(obj, v);
2012 * Creates the relationship list with the filtered node types.
2015 * @param obj the obj
2016 * @param cleanUp the clean up
2017 * @return the object
2018 * @throws InstantiationException the instantiation exception
2019 * @throws IllegalAccessException the illegal access exception
2020 * @throws IllegalArgumentException the illegal argument exception
2021 * @throws InvocationTargetException the invocation target exception
2022 * @throws NoSuchMethodException the no such method exception
2023 * @throws SecurityException the security exception
2024 * @throws UnsupportedEncodingException the unsupported encoding exception
2025 * @throws AAIException the AAI exception
2026 * @throws MalformedURLException the malformed URL exception
2027 * @throws URISyntaxException
2029 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException {
2030 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2032 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2033 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2034 return filterNodes.parallelStream().anyMatch(node::contains);
2038 List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices);
2040 //items.parallelStream().anyMatch(inputStr::contains)
2041 List<Object> relationshipObjList = obj.getValue("relationship");
2042 for (Vertex cousin : cousins) {
2044 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
2045 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
2046 if (result != null) {
2047 relationshipObjList.add(result);
2053 if (relationshipObjList.isEmpty()) {