2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
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;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import com.google.common.base.CaseFormat;
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.tinkerpop.gremlin.process.traversal.Path;
27 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
29 import org.apache.tinkerpop.gremlin.structure.Direction;
30 import org.apache.tinkerpop.gremlin.structure.Edge;
31 import org.apache.tinkerpop.gremlin.structure.Vertex;
32 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
33 import org.janusgraph.core.SchemaViolationException;
34 import org.javatuples.Pair;
35 import org.onap.aai.concurrent.AaiCallable;
36 import org.onap.aai.config.SpringContextAware;
37 import org.onap.aai.db.props.AAIProperties;
38 import org.onap.aai.edges.EdgeIngestor;
39 import org.onap.aai.edges.EdgeRule;
40 import org.onap.aai.edges.EdgeRuleQuery;
41 import org.onap.aai.edges.enums.AAIDirection;
42 import org.onap.aai.edges.enums.EdgeField;
43 import org.onap.aai.edges.enums.EdgeProperty;
44 import org.onap.aai.edges.enums.EdgeType;
45 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
46 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.introspection.*;
49 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
50 import org.onap.aai.introspection.sideeffect.*;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.logging.LogFormatTools;
53 import org.onap.aai.logging.StopWatch;
54 import org.onap.aai.parsers.query.QueryParser;
55 import org.onap.aai.parsers.relationship.RelationshipToURI;
56 import org.onap.aai.parsers.uri.URIParser;
57 import org.onap.aai.parsers.uri.URIToObject;
58 import org.onap.aai.parsers.uri.URIToRelationshipObject;
59 import org.onap.aai.query.builder.QueryBuilder;
60 import org.onap.aai.schema.enums.ObjectMetadata;
61 import org.onap.aai.schema.enums.PropertyMetadata;
62 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
63 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
64 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
65 import org.onap.aai.serialization.engines.query.QueryEngine;
66 import org.onap.aai.setup.SchemaVersion;
67 import org.onap.aai.setup.SchemaVersions;
68 import org.onap.aai.util.AAIConfig;
69 import org.onap.aai.util.AAIConstants;
70 import org.onap.aai.util.delta.*;
71 import org.onap.aai.workarounds.NamingExceptions;
72 import org.springframework.context.ApplicationContext;
74 import javax.ws.rs.core.UriBuilder;
75 import java.io.UnsupportedEncodingException;
76 import java.lang.reflect.Array;
77 import java.lang.reflect.InvocationTargetException;
78 import java.net.MalformedURLException;
80 import java.net.URISyntaxException;
82 import java.util.concurrent.ExecutionException;
83 import java.util.concurrent.ExecutorService;
84 import java.util.concurrent.Future;
85 import java.util.regex.Matcher;
86 import java.util.regex.Pattern;
88 public class DBSerializer {
90 private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class);
91 private static final String RELATIONSHIP_LABEL = "relationship-label";
92 private static final String RELATIONSHIP = "relationship";
93 public static final String FALSE = "false";
94 public static final String AAI_6145 = "AAI_6145";
95 public static final String AAI_6129 = "AAI_6129";
97 private final TransactionalGraphEngine engine;
98 private final String sourceOfTruth;
99 private final ModelType introspectionType;
100 private final SchemaVersion version;
101 private final Loader latestLoader;
102 private EdgeSerializer edgeSer;
103 private EdgeIngestor edgeRules;
104 private final Loader loader;
105 private final String baseURL;
106 private double dbTimeMsecs = 0;
107 private long currentTimeMillis;
109 private SchemaVersions schemaVersions;
110 private Set<String> namedPropNodes;
111 private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
112 private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
113 private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
114 private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>();
115 private int notificationDepth;
116 private boolean isDeltaEventsEnabled;
119 * Instantiates a new DB serializer.
121 * @param version the version
122 * @param engine the engine
123 * @param introspectionType the introspection type
124 * @param sourceOfTruth the source of truth
125 * @throws AAIException
127 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
128 String sourceOfTruth) throws AAIException {
129 this.engine = engine;
130 this.sourceOfTruth = sourceOfTruth;
131 this.introspectionType = introspectionType;
132 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
133 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
135 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
136 this.version = version;
138 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
139 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
140 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
141 this.currentTimeMillis = System.currentTimeMillis();
142 // If creating the DBSerializer the old way then set the notification depth to maximum
143 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
147 public DBSerializer(SchemaVersion version,
148 TransactionalGraphEngine engine,
149 ModelType introspectionType,
150 String sourceOfTruth,
151 int notificationDepth) throws AAIException {
152 this.engine = engine;
153 this.sourceOfTruth = sourceOfTruth;
154 this.introspectionType = introspectionType;
155 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
156 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
158 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
159 this.version = version;
161 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
162 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
163 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
164 this.currentTimeMillis = System.currentTimeMillis();
165 this.notificationDepth = notificationDepth;
169 private void initBeans() {
170 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
171 ApplicationContext ctx = SpringContextAware.getApplicationContext();
172 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
174 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
175 setEdgeSerializer(es);
176 isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
179 public void setEdgeSerializer(EdgeSerializer edgeSer) {
180 this.edgeSer = edgeSer;
183 public EdgeSerializer getEdgeSeriailizer() {
187 public void setEdgeIngestor(EdgeIngestor ei) {
191 public EdgeIngestor getEdgeIngestor() {
192 return this.edgeRules;
195 public Map<Vertex, Boolean> getUpdatedVertexes() {
196 return updatedVertexes;
199 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair(){
200 return impliedDeleteUriObjectPair;
204 * Touch standard vertex properties.
206 * @param isNewVertex the is new vertex
208 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
209 String timeNowInSec = Long.toString(currentTimeMillis);
211 String uuid = UUID.randomUUID().toString();
212 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
213 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
214 v.property(AAIProperties.AAI_UUID, uuid);
215 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
216 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
217 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
219 if(isDeltaEventsEnabled) {
220 standardVertexPropsDeltas(v, timeNowInSec);
222 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
223 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
224 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
228 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
229 String uri = v.property(AAIProperties.AAI_URI).value().toString();
230 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
231 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
232 if (getObjectDeltas().containsKey(uri)) {
233 getObjectDeltas().get(uri).setAction(objDeltaAction);
236 addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()), objDeltaAction);
237 addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()), objDeltaAction);
238 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()), objDeltaAction);
239 addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()), objDeltaAction);
241 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
244 AAIProperties.RESOURCE_VERSION,
245 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
250 AAIProperties.LAST_MOD_TS,
251 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
256 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
257 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
261 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
262 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
263 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
267 public Map<String, ObjectDelta> getObjectDeltas() {return objectDeltas;}
269 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
270 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
271 objectDelta.addPropertyDelta(prop, delta);
272 objectDeltas.put(uri, objectDelta);
275 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
276 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
277 objectDelta.addRelationshipDelta(delta);
278 objectDeltas.put(uri, objectDelta);
281 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
282 v.property(AAIProperties.NODE_TYPE, nodeType);
283 touchStandardVertexProperties(v, isNewVertex);
287 * Creates the new vertex.
289 * @param wrappedObject the wrapped object
292 public Vertex createNewVertex(Introspector wrappedObject) {
295 StopWatch.conditionalStart();
296 v = this.engine.tx().addVertex(wrappedObject.getDbName());
297 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
299 dbTimeMsecs += StopWatch.stopIfStarted();
307 * @param className the class name
311 * Removes the classpath from a class name
313 public String trimClassName(String className) {
314 String returnValue = "";
316 if (className.lastIndexOf('.') == -1) {
319 returnValue = className.substring(className.lastIndexOf('.') + 1);
329 * @param uriQuery the uri query
330 * @param identifier the identifier
331 * @throws SecurityException the security exception
332 * @throws IllegalArgumentException the illegal argument exception
333 * @throws AAIException the AAI exception
334 * @throws UnsupportedEncodingException the unsupported encoding exception
336 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
337 String requestContext) throws AAIException, UnsupportedEncodingException {
338 StopWatch.conditionalStart();
340 if (uriQuery.isDependent()) {
341 // try to find the parent
342 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
343 if (!vertices.isEmpty()) {
344 Vertex parent = vertices.get(0);
345 this.reflectDependentVertex(parent, v, obj, requestContext);
347 dbTimeMsecs += StopWatch.stopIfStarted();
348 throw new AAIException("AAI_6114",
349 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
352 serializeSingleVertex(v, obj, requestContext);
355 } catch (SchemaViolationException e) {
356 dbTimeMsecs += StopWatch.stopIfStarted();
357 throw new AAIException("AAI_6117", e);
359 dbTimeMsecs += StopWatch.stopIfStarted();
362 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
363 throws UnsupportedEncodingException, AAIException {
364 StopWatch.conditionalStart();
366 boolean isTopLevel = obj.isTopLevel();
368 addUriIfNeeded(v, obj.getURI());
371 URI uri = this.getURIForVertex(v);
372 URIParser parser = new URIParser(this.loader, uri);
373 if (parser.validate()) {
374 addUriIfNeeded(v, uri.toString());
377 processObject(obj, v, requestContext);
378 } catch (SchemaViolationException e) {
379 throw new AAIException("AAI_6117", e);
381 dbTimeMsecs += StopWatch.stopIfStarted();
385 private void addUriIfNeeded(Vertex v, String uri) {
386 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
387 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
388 v.property(AAIProperties.AAI_URI, uri);
398 * @throws IllegalArgumentException the illegal argument exception
399 * @throws SecurityException the security exception
400 * @throws AAIException the AAI exception
401 * @throws UnsupportedEncodingException the unsupported encoding exception
404 * Helper method for reflectToDb
405 * Handles all the property setting
407 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
408 throws UnsupportedEncodingException, AAIException {
409 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
410 properties.remove(AAIProperties.RESOURCE_VERSION);
411 List<Vertex> dependentVertexes = new ArrayList<>();
412 List<Vertex> processedVertexes = new ArrayList<>();
414 boolean isComplexType ;
417 // If the notification depth is set to maximum
418 // this is the behavior of the expected clients
419 if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
420 if (!obj.isContainer()) {
421 this.touchStandardVertexProperties(v, false);
424 this.executePreSideEffects(obj, v);
425 for (String property : properties) {
426 final String propertyType;
427 propertyType = obj.getType(property);
428 isComplexType = obj.isComplexType(property);
429 isListType = obj.isListType(property);
430 Object value = obj.getValue(property);
432 if (!(isComplexType || isListType)) {
433 boolean canModify = this.canModify(obj, property, requestContext);
436 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
437 String dbProperty = property;
438 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
439 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
441 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
442 // data linked properties are ephemeral
443 // they are populated dynamically on GETs
446 Object oldValue = v.property(dbProperty).orElse(null);
447 String uri = getURIForVertex(v).toString();
449 if (!value.equals(oldValue)) {
450 if (propertyType.toLowerCase().contains(".long")) {
451 v.property(dbProperty, new Integer(((Long) value).toString()));
453 v.property(dbProperty, value);
455 if (isDeltaEventsEnabled) {
456 createDeltaProperty(uri, value, dbProperty, oldValue);
458 this.updatedVertexes.putIfAbsent(v, false);
461 if (oldValue != null) {
462 v.property(dbProperty).remove();
463 if (isDeltaEventsEnabled) {
464 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
466 this.updatedVertexes.putIfAbsent(v, false);
470 } else if (isListType) {
471 List<Object> list = (List<Object>) value;
472 if (obj.isComplexGenericType(property)) {
474 for (Object o : list) {
475 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
476 child.setURIChain(obj.getURI());
477 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
482 if (isDeltaEventsEnabled) {
483 String uri = getURIForVertex(v).toString();
484 List<Object> oldVal = engine.getListProperty(v, property);
485 engine.setListProperty(v, property, list);
486 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
487 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
488 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
490 } else { // is either a create or update and is handled by the called method
491 createDeltaProperty(uri, list, property, oldVal);
494 engine.setListProperty(v, property, list);
496 this.updatedVertexes.putIfAbsent(v, false);
499 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
500 // back to this method
501 if (value != null) { // effectively ignore complex properties not included in the object we're
503 if (value.getClass().isArray()) {
505 int length = Array.getLength(value);
506 for (int i = 0; i < length; i++) {
507 Object arrayElement = Array.get(value, i);
508 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
509 child.setURIChain(obj.getURI());
510 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
513 } else if (!property.equals("relationship-list")) {
515 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
516 if (introspector.isContainer()) {
517 dependentVertexes.addAll(
518 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
519 introspector.setURIChain(obj.getURI());
521 processedVertexes.addAll(processObject(introspector, v, requestContext));
524 dependentVertexes.addAll(
525 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
526 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
529 } else if (property.equals("relationship-list")) {
530 handleRelationships(obj, v);
535 this.writeThroughDefaults(v, obj);
536 /* handle those vertexes not touched */
537 for (Vertex toBeKept : processedVertexes) {
538 dependentVertexes.remove(toBeKept);
541 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
542 List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
544 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
545 for(Vertex curVertex : impliedDeleteVertices){
546 if(!curVertex.property("aai-uri").isPresent()){
547 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
550 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
551 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
553 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
555 if(!curObj.isTopLevel()){
556 curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
559 if(!impliedDeleteUriObjectPair.containsKey(curAaiUri)){
560 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
565 impliedDelete.delete(impliedDeleteVertices);
567 // touch svp using vertex list for what changed
568 // if the notification depth is zero
569 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
570 this.updatedVertexes.entrySet().stream()
571 .filter(e -> !e.getValue())
572 .filter(e -> !edgeVertexes.contains(e.getKey()))
574 this.touchStandardVertexProperties(e.getKey(), false);
578 this.executePostSideEffects(obj, v);
579 return processedVertexes;
582 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
583 if (oldValue == null) {
584 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
586 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
590 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
591 Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
593 HashMap<String, Introspector> relatedVertices = new HashMap<>();
594 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
596 if (!aaiUriProperty.isPresent()) {
597 if (LOGGER.isDebugEnabled()) {
598 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
602 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
604 return relatedVertices;
607 String aaiUri = aaiUriProperty.value().toString();
609 if (!obj.isTopLevel()) {
610 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
611 List<Vertex> vertexChain;
612 // If the uriList is null then there is something wrong with converting the uri
613 // into a list of aai-uris so falling back to the old mechanism for finding parents
614 if (uriList == null) {
616 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
617 vertexChain = queryEngine.findParents(v);
618 } else if (uriList.length == 1) {
619 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
620 vertexChain = Collections.singletonList(v);
622 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
623 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
624 // inject v into start of vertexChain
625 vertexChain.add(0, v);
627 for (Vertex vertex : vertexChain) {
629 final Introspector vertexObj = this.getVertexProperties(vertex);
630 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
631 } catch (AAIUnknownObjectException e) {
632 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
637 final Introspector vertexObj = this.getVertexProperties(v);
638 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
639 } catch (AAIUnknownObjectException e) {
640 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
644 return relatedVertices;
648 * Given an uri, introspector object and loader object
649 * it will check if the obj is top level object if it is,
650 * it will return immediately returning the uri passed in
651 * If it isn't, it will go through, get the uriTemplate
652 * from the introspector object and get the count of "/"s
653 * and remove that part of the uri using substring
654 * and keep doing that until the current object is top level
655 * Also added the max depth just so worst case scenario
656 * Then keep adding aai-uri to the list include the aai-uri passed in
657 * Convert that list into an array and return it
664 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
666 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
667 * it converts to /vservers/vserver
669 * lastIndexOf /vservers/vserver in
670 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
675 * Use substring to get the string from 0 to that lastIndexOf
676 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
678 * From this new aai-uri, generate a introspector from the URITOObject class
679 * and keep doing this until you
683 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
684 * @param obj - introspector object of the given starting vertex
685 * @param loader - Type of loader which will always be MoxyLoader to support model driven
686 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
687 * @throws UnsupportedEncodingException
688 * @throws AAIException
690 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
691 throws UnsupportedEncodingException, AAIException {
693 List<String> uriList = new ArrayList<>();
695 String truncatedUri = aaiUri;
696 int depth = AAIProperties.MAXIMUM_DEPTH;
697 uriList.add(truncatedUri);
699 while (depth >= 0 && !obj.isTopLevel()) {
700 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
702 if (template == null) {
703 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
707 int templateCount = StringUtils.countMatches(template, "/");
708 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
710 if (templateCount > truncatedUriCount) {
711 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
715 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
716 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
717 uriList.add(truncatedUri);
718 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
722 return uriList.toArray(new String[0]);
726 * Handle relationships.
729 * @param vertex the vertex
730 * @throws SecurityException the security exception
731 * @throws IllegalArgumentException the illegal argument exception
732 * @throws UnsupportedEncodingException the unsupported encoding exception
733 * @throws AAIException the AAI exception
736 * Handles the explicit relationships defined for an obj
738 private void handleRelationships(Introspector obj, Vertex vertex)
739 throws UnsupportedEncodingException, AAIException {
741 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
742 processRelationshipList(wrappedRl, vertex);
747 * Process relationship list.
749 * @param wrapped the wrapped
751 * @throws UnsupportedEncodingException the unsupported encoding exception
752 * @throws AAIException the AAI exception
754 private void processRelationshipList(Introspector wrapped, Vertex v)
755 throws UnsupportedEncodingException, AAIException {
757 List<Object> relationships = wrapped.getValue("relationship");
758 String mainUri = getURIForVertex(v).toString();
759 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
760 EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
761 .edgeType(EdgeType.COUSIN)
762 .version(wrapped.getVersion());
763 EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
764 .edgeType(EdgeType.TREE)
765 .version(wrapped.getVersion());
767 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
769 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
770 for (Object relationship : relationships) {
773 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
774 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
776 if (relUri.startsWith("/vnf/")) {
777 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
778 List<Vertex> results = parser.getQueryBuilder().toList();
779 if (results.isEmpty()) {
780 final AAIException ex = new AAIException(AAI_6129,
781 String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
782 ex.getTemplateVars().add(parser.getResultType());
783 ex.getTemplateVars().add(parser.getUri().toString());
786 // still an issue if there's more than one
787 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
788 relUri = results.get(0).value(AAIProperties.AAI_URI);
790 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
796 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
797 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
799 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
800 String bNodeType = uriToObject.getEntityName();
801 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
802 if (!edgeIngestor.hasRule(ruleQuery)) {
803 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
804 if (edgeIngestor.hasRule(treeQuery)) {
805 throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
807 throw new AAIException("AAI_6120", String.format(
808 "No EdgeRule found for passed nodeTypes: %s, %s.",
809 aNodeType, bNodeType));
812 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
813 if (rules.size() == 1) {
814 label = rules.get(0).getLabel();
816 Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
817 if (defaultRule.isPresent()) {
818 label = defaultRule.get().getLabel();
820 throw new AAIException(AAI_6145);
823 } catch (EdgeRuleNotFoundException ea) {
824 throw new AAIException(AAI_6145, ea);
828 cousinUriAndLabels.add(Pair.with(relUri, label));
831 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
832 Set<Path> toRemove = new HashSet<>();
835 // for each path 3 things can happen:
836 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
837 // 2. The edge rule exits in this version it's included in the request the edge is left alone
838 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
839 for (Path path : paths) {
840 if (path.size() < 3) {
845 // v ----related-to--> otherV
846 // In the above case,
847 // path objects get(0) returns vertex v
848 // path objects.get(1) returns edge related-to
849 // path objects.get(2) returns vertex otherV
850 Vertex otherV = path.get(2);
853 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
854 bUri = otherV.value(AAIProperties.AAI_URI);
858 String edgeLabel = path.<Edge>get(1).label();
860 Pair<String, String> key = Pair.with(bUri, edgeLabel);
861 if (cousinUriAndLabels.contains(key)) {
862 cousinUriAndLabels.remove(key);
865 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
866 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
870 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
871 if (edgeIngestor.hasRule(ruleQuery)) {
878 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
879 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
882 String label = cousinUriAndLabel.getValue1();
883 String cousinUri = cousinUriAndLabel.getValue0();
884 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
886 List<Vertex> results = parser.getQueryBuilder().toList();
887 if (results.isEmpty()) {
888 final AAIException ex = new AAIException(AAI_6129,
889 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
890 ex.getTemplateVars().add(parser.getResultType());
891 ex.getTemplateVars().add(parser.getUri().toString());
894 // still an issue if there's more than one
895 cousinVertex = results.get(0);
898 if (cousinVertex != null) {
899 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
900 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
901 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
903 if (!edgeRules.hasRule(baseQ.build())) {
904 throw new AAIException("AAI_6120", String.format(
905 "No EdgeRule found for passed nodeTypes: %s, %s%s.",
906 aNodeType, cousinType, label != null ? (" with label " + label) : ""));
907 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
908 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
909 throw new AAIException(AAI_6145);
912 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
915 toBeCreated.add(Pair.with(cousinVertex, label));
920 for (Path path : toRemove) {
921 if(isDeltaEventsEnabled) {
922 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
924 this.updatedVertexes.putIfAbsent(v, false);
925 this.edgeVertexes.add(path.get(2));
926 path.<Edge>get(1).remove();
929 for (Pair<Vertex, String> create : toBeCreated) {
931 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
932 if (isDeltaEventsEnabled) {
933 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
935 this.updatedVertexes.putIfAbsent(v, false);
936 this.edgeVertexes.add(create.getValue0());
937 } catch (NoEdgeRuleFoundException ex) {
938 throw new AAIException(AAI_6129, ex);
944 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
945 RelationshipDelta relationshipDelta = new RelationshipDelta(
947 edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
948 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
949 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
950 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
952 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
953 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
957 * Write through defaults.
961 * @throws AAIUnknownObjectException
963 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
964 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
965 if (latest != null) {
966 Set<String> required = latest.getRequiredProperties();
968 for (String field : required) {
969 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
970 if (defaultValue != null) {
971 Object vertexProp = v.property(field).orElse(null);
972 if (vertexProp == null) {
973 v.property(field, defaultValue);
982 * Reflect dependent vertex.
985 * @param dependentObj the dependent obj
987 * @throws IllegalArgumentException the illegal argument exception
988 * @throws SecurityException the security exception
989 * @throws AAIException the AAI exception
990 * @throws UnsupportedEncodingException the unsupported encoding exception
991 * @throws AAIUnknownObjectException
993 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
994 throws AAIException, UnsupportedEncodingException {
996 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
997 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
998 query.createKeyQuery(dependentObj);
1000 List<Vertex> items = query.toList();
1002 Vertex dependentVertex;
1003 if (items.size() == 1) {
1004 dependentVertex = items.get(0);
1005 this.verifyResourceVersion("update", dependentObj.getDbName(),
1006 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1007 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1009 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1010 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1011 dependentVertex = createNewVertex(dependentObj);
1014 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1019 * Reflect dependent vertex.
1021 * @param parent the parent
1022 * @param child the child
1023 * @param obj the obj
1024 * @return the vertex
1025 * @throws IllegalArgumentException the illegal argument exception
1026 * @throws SecurityException the security exception
1027 * @throws AAIException the AAI exception
1028 * @throws UnsupportedEncodingException the unsupported encoding exception
1029 * @throws AAIUnknownObjectException
1031 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1032 throws AAIException, UnsupportedEncodingException {
1034 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1035 if (parentUri != null) {
1038 addUriIfNeeded(child, parentUri + uri);
1040 processObject(obj, child, requestContext);
1043 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1046 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1047 if (canBeLinked != null && canBeLinked.equals("true")) {
1048 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1049 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1050 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1051 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1053 child.property(AAIProperties.LINKED, true);
1056 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1057 if(isDeltaEventsEnabled) {
1058 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1065 private SchemaVersion getVerForContext(String requestContext) {
1066 Pattern pattern = Pattern.compile("v[0-9]+");
1067 Matcher m = pattern.matcher(requestContext);
1069 return this.version;
1071 return new SchemaVersion(requestContext);
1078 * @param vertices the vertices
1079 * @param obj the obj
1080 * @param depth the depth
1081 * @param cleanUp the clean up
1082 * @return the introspector
1083 * @throws AAIException the AAI exception
1084 * @throws IllegalArgumentException the illegal argument exception
1085 * @throws SecurityException the security exception
1086 * @throws UnsupportedEncodingException the unsupported encoding exception
1087 * @throws AAIUnknownObjectException
1088 * @throws URISyntaxException
1090 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1091 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1092 final int internalDepth;
1093 if (depth == Integer.MAX_VALUE) {
1094 internalDepth = depth--;
1096 internalDepth = depth;
1098 StopWatch.conditionalStart();
1099 if (vertices.size() > 1 && !obj.isContainer()) {
1100 dbTimeMsecs += StopWatch.stopIfStarted();
1101 throw new AAIException("AAI_6136",
1102 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1103 } else if (obj.isContainer()) {
1105 String listProperty = null;
1106 for (String property : obj.getProperties()) {
1107 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1108 listProperty = property;
1112 final String propertyName = listProperty;
1113 getList = obj.getValue(listProperty);
1116 * This is an experimental multithreading experiment
1119 ExecutorService pool = GetAllPool.getInstance().getPool();
1121 List<Future<Object>> futures = new ArrayList<>();
1123 for (Vertex v : vertices) {
1124 AaiCallable<Object> task = new AaiCallable<Object>() {
1126 public Object process() throws UnsupportedEncodingException, AAIException {
1127 Set<Vertex> seen = new HashSet<>();
1128 Introspector childObject;
1129 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1130 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1131 return childObject.getUnderlyingObject();
1134 futures.add(pool.submit(task));
1137 for (Future<Object> future : futures) {
1139 getList.add(future.get());
1140 } catch (ExecutionException | InterruptedException e) {
1141 dbTimeMsecs += StopWatch.stopIfStarted();
1142 throw new AAIException("AAI_4000", e);
1145 } else if (vertices.size() == 1) {
1146 Set<Vertex> seen = new HashSet<>();
1147 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1152 dbTimeMsecs += StopWatch.stopIfStarted();
1159 * @param vertices the vertices
1160 * @param obj the obj
1161 * @param depth the depth
1162 * @param cleanUp the clean up
1163 * @return the introspector
1164 * @throws AAIException the AAI exception
1165 * @throws IllegalArgumentException the illegal argument exception
1166 * @throws SecurityException the security exception
1167 * @throws UnsupportedEncodingException the unsupported encoding exception
1168 * @throws AAIUnknownObjectException
1169 * @throws URISyntaxException
1171 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1172 String cleanUp) throws UnsupportedEncodingException, AAIException {
1173 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1179 * @param obj the obj
1181 * @param seen the seen
1182 * @param depth the depth
1183 * @param cleanUp the clean up
1184 * @return the introspector
1185 * @throws IllegalArgumentException the illegal argument exception
1186 * @throws SecurityException the security exception
1187 * @throws UnsupportedEncodingException the unsupported encoding exception
1188 * @throws AAIException the AAI exception
1189 * @throws AAIUnknownObjectException
1190 * @throws URISyntaxException
1192 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1193 String cleanUp) throws AAIException, UnsupportedEncodingException {
1194 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1200 * @param obj the obj
1202 * @param seen the seen
1203 * @param depth the depth
1204 * @param cleanUp the clean up
1205 * @return the introspector
1206 * @throws IllegalArgumentException the illegal argument exception
1207 * @throws SecurityException the security exception
1208 * @throws UnsupportedEncodingException the unsupported encoding exception
1209 * @throws AAIException the AAI exception
1210 * @throws AAIUnknownObjectException
1211 * @throws URISyntaxException
1213 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1214 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1222 boolean modified = false;
1223 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1224 List<Object> getList = null;
1226 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1227 this.copySimpleProperty(property, obj, v);
1230 if (obj.isComplexType(property)) {
1231 /* container case */
1233 if (!property.equals("relationship-list") && depth >= 0) {
1234 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1235 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1236 if (result != null) {
1237 obj.setValue(property, argumentObject.getUnderlyingObject());
1240 } else if (property.equals("relationship-list") && !nodeOnly) {
1241 /* relationships need to be handled correctly */
1242 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1243 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1244 if (relationshipList != null) {
1245 obj.setValue(property, relationshipList.getUnderlyingObject());
1250 } else if (obj.isListType(property)) {
1252 if (property.equals("any")) {
1255 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1256 if (obj.isComplexGenericType(property) && depth >= 0) {
1257 final String childDbName = convertFromCamelCase(genericType);
1258 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1262 rule = edgeRules.getRule(
1263 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1264 } catch (EdgeRuleNotFoundException e) {
1265 throw new NoEdgeRuleFoundException(e);
1266 } catch (AmbiguousRuleChoiceException e) {
1267 throw new MultipleEdgeRuleFoundException(e);
1269 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1271 Direction ruleDirection = rule.getDirection();
1272 List<Vertex> verticesList = new ArrayList<>();
1273 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1274 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1275 verticesList.add(vertex);
1278 if (!verticesList.isEmpty()) {
1279 getList = obj.getValue(property);
1282 for (Vertex childVertex : verticesList) {
1283 if (!seen.contains(childVertex)) {
1284 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1287 dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1288 if (result != null) {
1289 getList.add(argumentObject.getUnderlyingObject());
1294 LOGGER.warn("Cycle found while serializing vertex id={}",
1295 childVertex.id().toString());
1298 if (processed == 0) {
1299 // vertices were all seen, reset the list
1302 if (processed > 0) {
1306 } else if (obj.isSimpleGenericType(property)) {
1307 List<Object> temp = this.engine.getListProperty(v, property);
1309 getList = (List<Object>) obj.getValue(property);
1310 getList.addAll(temp);
1318 // no changes were made to this obj, discard the instance
1322 this.enrichData(obj, v);
1327 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1328 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1329 if (nodeType == null) {
1330 throw new AAIException("AAI_6143");
1333 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1334 Set<Vertex> seen = new HashSet<>();
1336 StopWatch.conditionalStart();
1337 this.dbToObject(obj, v, seen, depth, false, FALSE);
1338 dbTimeMsecs += StopWatch.stopIfStarted();
1343 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1344 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1347 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1348 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1349 if (nodeType == null) {
1350 throw new AAIException("AAI_6143");
1352 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1353 Set<Vertex> seen = new HashSet<>();
1354 StopWatch.conditionalStart();
1355 this.dbToObject(obj, v, seen, depth, false, FALSE);
1356 dbTimeMsecs += StopWatch.stopIfStarted();
1361 * Copy simple property.
1363 * @param property the property
1364 * @param obj the obj
1366 * @throws IllegalArgumentException the illegal argument exception
1367 * @throws SecurityException the security exception
1369 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1370 final Object temp = getProperty(obj, property, v);
1372 obj.setValue(property, temp);
1376 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1378 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1379 String[] simplePropsArray = new String[simpleProperties.size()];
1380 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1382 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1384 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1386 return simplePropsHashMap;
1389 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1390 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1391 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1392 return relationshipList;
1396 * Creates the relationship list.
1399 * @param obj the obj
1400 * @param cleanUp the clean up
1401 * @return the object
1402 * @throws IllegalArgumentException the illegal argument exception
1403 * @throws SecurityException the security exception
1404 * @throws UnsupportedEncodingException the unsupported encoding exception
1405 * @throws AAIException the AAI exception
1406 * @throws URISyntaxException
1408 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1409 throws UnsupportedEncodingException, AAIException {
1410 // default boolean value for isSkipRelatedTo is false
1411 return createRelationshipList(v, obj, cleanUp, false);
1415 * Creates the relationship list.
1418 * @param obj the obj
1419 * @param cleanUp the clean up
1420 * @param isSkipRelatedTo to determine adding related-to-property in response
1421 * @return the object
1422 * @throws IllegalArgumentException the illegal argument exception
1423 * @throws SecurityException the security exception
1424 * @throws UnsupportedEncodingException the unsupported encoding exception
1425 * @throws AAIException the AAI exception
1426 * @throws URISyntaxException
1428 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1429 throws UnsupportedEncodingException, AAIException {
1431 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1432 VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1434 if (!nodeTypeProperty.isPresent()) {
1435 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1439 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1441 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1443 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1445 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1446 .edgeType(EdgeType.COUSIN)
1447 .version(obj.getVersion());
1449 for (Path path : paths){
1450 if(path.size() < 3){
1455 // v ----related-to--> otherV
1456 // In the above case,
1457 // path objects get(0) returns vertex v
1458 // path objects.get(1) returns edge related-to
1459 // path objects.get(2) returns vertex otherV
1460 Edge edge = path.get(1);
1461 Vertex otherV= path.get(2);
1463 // TODO: Come back and revisit this code
1464 // Create a query based on the a nodetype and b nodetype
1465 // which is also a cousin edge and ensure the version
1466 // is used properly so for example in order to be backwards
1467 // compatible if we had allowed a edge between a and b
1468 // in a previous release and we decided to remove it from
1469 // the edge rules in the future we can display the edge
1470 // only for the older apis and the new apis if the edge rule
1471 // is removed will not be seen in the newer version of the API
1473 String bNodeType = null;
1474 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1475 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
1480 String edgeLabel = edge.label();
1481 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1483 if(!edgeIngestor.hasRule(ruleQuery)){
1484 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1488 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1489 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1490 if (result != null) {
1491 relationshipObjList.add(result);
1496 if (relationshipObjList.isEmpty()) {
1504 * Process edge relationship.
1506 * @param relationshipObj the relationship obj
1507 * @param edgeLabel the edge's label
1508 * @param cleanUp the clean up
1509 * @return the object
1510 * @throws IllegalArgumentException the illegal argument exception
1511 * @throws SecurityException the security exception
1512 * @throws UnsupportedEncodingException the unsupported encoding exception
1513 * @throws AAIUnknownObjectException
1514 * @throws URISyntaxException
1516 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1517 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1519 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1521 if (!aaiUriProperty.isPresent()) {
1525 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1527 URIToRelationshipObject uriParser;
1528 Introspector result;
1530 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1531 result = uriParser.getResult();
1532 } catch (AAIException | URISyntaxException e) {
1533 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1534 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1538 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1540 if (cousinVertexNodeType.isPresent()) {
1541 String cousinType = cousinVertexNodeType.value().toString();
1542 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1543 this.addRelatedToProperty(result, cousin, cousinType);
1547 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1548 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1551 return result.getUnderlyingObject();
1555 * Gets the URI for vertex.
1558 * @return the URI for vertex
1559 * @throws IllegalArgumentException the illegal argument exception
1560 * @throws SecurityException the security exception
1561 * @throws UnsupportedEncodingException the unsupported encoding exception
1562 * @throws AAIUnknownObjectException
1564 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1566 return getURIForVertex(v, false);
1569 public URI getURIForVertex(Vertex v, boolean overwrite) {
1570 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1572 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1574 if (aaiUri != null && !overwrite) {
1575 uri = UriBuilder.fromPath(aaiUri).build();
1582 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1583 throws AAIUnknownObjectException {
1588 obj = this.loader.introspectorFromName(cousinType);
1589 } catch (AAIUnknownObjectException ex) {
1590 if (LOGGER.isTraceEnabled()) {
1591 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1592 cousinType, cousinVertex.id());
1597 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1598 List<Introspector> relatedToProperties = new ArrayList<>();
1600 if (nameProps != null) {
1601 String[] props = nameProps.split(",");
1602 for (String prop : props) {
1603 final Object temp = getProperty(obj, prop, cousinVertex);
1604 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1605 relatedTo.setValue("property-key", cousinType + "." + prop);
1606 relatedTo.setValue("property-value", temp);
1607 relatedToProperties.add(relatedTo);
1611 if (!relatedToProperties.isEmpty()) {
1612 List<Object> relatedToList = relationship.getValue("related-to-property");
1613 for (Introspector introspector : relatedToProperties) {
1614 relatedToList.add(introspector.getUnderlyingObject());
1620 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1622 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1623 String dbPropertyName = prop;
1625 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1626 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1629 return vertex.<Object>property(dbPropertyName).orElse(null);
1635 * @param relationship the relationship
1636 * @param inputVertex the input vertex
1637 * @return true, if successful
1638 * @throws UnsupportedEncodingException the unsupported encoding exception
1639 * @throws AAIException the AAI exception
1641 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1642 throws UnsupportedEncodingException, AAIException {
1644 Vertex relatedVertex;
1645 StopWatch.conditionalStart();
1646 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1648 String label = null;
1649 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1650 label = relationship.getValue(RELATIONSHIP_LABEL);
1653 List<Vertex> results = parser.getQueryBuilder().toList();
1654 if (results.isEmpty()) {
1655 dbTimeMsecs += StopWatch.stopIfStarted();
1656 AAIException e = new AAIException(AAI_6129,
1657 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1658 e.getTemplateVars().add(parser.getResultType());
1659 e.getTemplateVars().add(parser.getUri().toString());
1662 // still an issue if there's more than one
1663 relatedVertex = results.get(0);
1666 if (relatedVertex != null) {
1670 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1672 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1673 if(isDeltaEventsEnabled) {
1674 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1677 // attempted to link two vertexes already linked
1680 dbTimeMsecs += StopWatch.stopIfStarted();
1684 dbTimeMsecs += StopWatch.stopIfStarted();
1685 return relatedVertex;
1689 * Gets all the edges between of the type with the specified label.
1691 * @param aVertex the out vertex
1692 * @param bVertex the in vertex
1693 * @return the edges between
1695 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1699 if (bVertex != null) {
1700 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1701 if (EdgeType.TREE.equals(type)) {
1702 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1703 if (edgeRule.getDirection().equals(Direction.IN)) {
1704 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1705 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1706 .not(__.has(EdgeField.PRIVATE.toString(), true));
1708 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1709 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1710 .not(__.has(EdgeField.PRIVATE.toString(), true));
1712 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1714 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1715 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1716 .not(__.has(EdgeField.PRIVATE.toString(), true));
1717 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1719 List<Edge> list = findEdgesBetween.toList();
1720 if (!list.isEmpty()) {
1721 result = list.get(0);
1729 * Gets all the edges string between of the type.
1731 * @param aVertex the out vertex
1732 * @param bVertex the in vertex
1733 * @return the edges between
1734 * @throws NoEdgeRuleFoundException
1736 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1738 List<String> result = new ArrayList<>();
1740 if (bVertex != null) {
1741 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1742 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1743 if (EdgeType.TREE.equals(type)) {
1744 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1745 __.has(EdgeField.PRIVATE.toString(), true)));
1747 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1748 .not(__.has(EdgeField.PRIVATE.toString(), true));
1750 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1751 result = findEdgesBetween.label().toList();
1757 * Gets all the edges between the vertexes with the label and type.
1759 * @param aVertex the out vertex
1760 * @param bVertex the in vertex
1762 * @return the edges between
1763 * @throws AAIException the AAI exception
1765 private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1769 if (bVertex != null) {
1770 String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1771 String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1772 EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1775 rule = edgeRules.getRule(q);
1776 } catch (EdgeRuleNotFoundException e) {
1777 throw new NoEdgeRuleFoundException(e);
1778 } catch (AmbiguousRuleChoiceException e) {
1779 throw new MultipleEdgeRuleFoundException(e);
1781 edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1788 * Gets the edge between with the label and edge type.
1790 * @param aVertex the out vertex
1791 * @param bVertex the in vertex
1793 * @return the edge between
1794 * @throws AAIException the AAI exception
1795 * @throws NoEdgeRuleFoundException
1797 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1799 StopWatch.conditionalStart();
1800 if (bVertex != null) {
1802 Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1804 dbTimeMsecs += StopWatch.stopIfStarted();
1809 dbTimeMsecs += StopWatch.stopIfStarted();
1813 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1814 return this.getEdgeBetween(type, aVertex, bVertex, null);
1820 * @param relationship the relationship
1821 * @param inputVertex the input vertex
1822 * @return true, if successful
1823 * @throws UnsupportedEncodingException the unsupported encoding exception
1824 * @throws AAIException the AAI exception
1826 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1827 throws UnsupportedEncodingException, AAIException {
1829 Vertex relatedVertex;
1830 StopWatch.conditionalStart();
1831 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1833 List<Vertex> results = parser.getQueryBuilder().toList();
1835 String label = null;
1836 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1837 label = relationship.getValue(RELATIONSHIP_LABEL);
1840 if (results.isEmpty()) {
1841 dbTimeMsecs += StopWatch.stopIfStarted();
1842 return Optional.empty();
1845 relatedVertex = results.get(0);
1848 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1849 } catch (NoEdgeRuleFoundException e) {
1850 dbTimeMsecs += StopWatch.stopIfStarted();
1851 throw new AAIException(AAI_6129, e);
1854 if(isDeltaEventsEnabled) {
1855 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1856 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1859 dbTimeMsecs += StopWatch.stopIfStarted();
1860 return Optional.of(relatedVertex);
1862 dbTimeMsecs += StopWatch.stopIfStarted();
1863 return Optional.empty();
1869 * Delete with traversal.
1871 * @param startVertex the start vertex
1873 public void deleteWithTraversal(Vertex startVertex) {
1874 StopWatch.conditionalStart();
1875 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1876 this.delete(results);
1880 * Removes the list of vertexes from the graph
1882 * Current the vertex label will just be vertex but
1883 * in the future the aai-node-type property will be replaced
1884 * by using the vertex label as when retrieving an vertex
1885 * and retrieving an single property on an vertex will pre-fetch
1886 * all the properties of that vertex and this is due to the following property
1889 * query.fast-property=true
1892 * JanusGraph doesn't provide the capability to override that
1893 * at a transaction level and there is a plan to move to vertex label
1894 * so it is best to utilize this for now and when the change is applied
1896 * @param vertices - list of vertices to delete from the graph
1898 void delete(List<Vertex> vertices) {
1899 StopWatch.conditionalStart();
1901 for (Vertex v : vertices) {
1902 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
1903 if(isDeltaEventsEnabled) {
1904 deltaForVertexDelete(v);
1906 //add the cousin vertexes of v to have their resource-version updated and notified on.
1907 v.edges(Direction.BOTH)
1908 .forEachRemaining(e -> {
1909 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
1910 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
1911 e.bothVertices().forEachRemaining(cousinV -> {
1912 if (!v.equals(cousinV)) {
1913 edgeVertexes.add(cousinV);
1919 //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
1920 // it should be removed from them as v is to be deleted
1921 edgeVertexes.remove(v);
1922 updatedVertexes.remove(v);
1926 dbTimeMsecs += StopWatch.stopIfStarted();
1929 private void deltaForVertexDelete(Vertex v) {
1930 String aaiUri = v.property(AAIProperties.AAI_URI).value().toString();
1931 v.keys().forEach(k -> {
1932 List<Object> list = new ArrayList<>();
1933 v.properties(k).forEachRemaining(vp -> list.add(vp.value()));
1934 if (list.size() == 1) {
1935 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)), DeltaAction.DELETE);
1937 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE);
1941 v.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
1948 * @param resourceVersion the resource version
1949 * @throws IllegalArgumentException the illegal argument exception
1950 * @throws AAIException the AAI exception
1951 * @throws InterruptedException the interrupted exception
1953 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
1954 throws IllegalArgumentException, AAIException {
1956 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1958 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1959 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
1960 * So its better to make these in 2 steps
1962 if (result && !deletableVertices.isEmpty()) {
1963 result = verifyPreventDeleteSemantics(deletableVertices);
1968 deleteWithTraversal(v);
1969 } catch (IllegalStateException e) {
1970 throw new AAIException("AAI_6110", e);
1980 * @param resourceVersion the resource version
1981 * @throws IllegalArgumentException the illegal argument exception
1982 * @throws AAIException the AAI exception
1983 * @throws InterruptedException the interrupted exception
1985 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion)
1986 throws IllegalArgumentException, AAIException {
1988 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1993 deleteWithTraversal(v);
1994 } catch (IllegalStateException e) {
1995 throw new AAIException("AAI_6110", e);
2002 * Verify delete semantics.
2004 * @param vertex the vertex
2005 * @param resourceVersion the resource version
2006 * @return true, if successful
2007 * @throws AAIException the AAI exception
2009 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2010 throws AAIException {
2013 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2014 if (enableResourceVersion) {
2015 this.verifyResourceVersion("delete", nodeType,
2016 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2018 List<Vertex> vertices = new ArrayList<>();
2019 vertices.add(vertex);
2020 result = verifyPreventDeleteSemantics(vertices);
2026 * Verify Prevent delete semantics.
2028 * @param vertices the list of vertices
2029 * @return true, if successful
2030 * @throws AAIException the AAI exception
2032 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2033 boolean result = true;
2034 String errorDetail = " unknown delete semantic found";
2035 String aaiExceptionCode = "";
2037 StopWatch.conditionalStart();
2039 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2040 * "prevent-delete" condition
2041 * If yes - that should prevent the deletion of the vertex
2042 * Dedup makes sure we dont capture the prevent-delete vertices twice
2043 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2046 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2047 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2048 .values(AAIProperties.NODE_TYPE),
2049 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2050 .values(AAIProperties.NODE_TYPE))
2053 dbTimeMsecs += StopWatch.stopIfStarted();
2054 if (!preventDeleteVertices.isEmpty()) {
2055 aaiExceptionCode = "AAI_6110";
2056 errorDetail = String.format(
2057 "Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s",
2058 preventDeleteVertices);
2062 throw new AAIException(aaiExceptionCode, errorDetail);
2068 * Verify resource version.
2070 * @param action the action
2071 * @param nodeType the node type
2072 * @param currentResourceVersion the current resource version
2073 * @param resourceVersion the resource version
2074 * @param uri the uri
2075 * @return true, if successful
2076 * @throws AAIException the AAI exception
2078 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2079 String resourceVersion, String uri) throws AAIException {
2080 String enabled = "";
2081 String errorDetail = "";
2082 String aaiExceptionCode = "";
2083 boolean isDeleteResourceVersionOk = true;
2084 if (currentResourceVersion == null) {
2085 currentResourceVersion = "";
2088 if (resourceVersion == null) {
2089 resourceVersion = "";
2092 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2094 } catch (AAIException e) {
2095 ErrorLogHelper.logException(e);
2097 if (enabled.equals("true")) {
2098 if ("delete".equals(action)) {
2099 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2101 if ((!isDeleteResourceVersionOk)
2102 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2103 if ("create".equals(action) && !resourceVersion.equals("")) {
2104 errorDetail = "resource-version passed for " + action + " of " + uri;
2105 aaiExceptionCode = "AAI_6135";
2106 } else if (resourceVersion.equals("")) {
2107 errorDetail = "resource-version not passed for " + action + " of " + uri;
2108 aaiExceptionCode = "AAI_6130";
2110 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2111 aaiExceptionCode = "AAI_6131";
2114 throw new AAIException(aaiExceptionCode, errorDetail);
2122 * Verify resource version for delete.
2124 * @param currentResourceVersion the current resource version
2125 * @param resourceVersion the resource version
2126 * @return true, if successful or false if there is a mismatch
2128 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2130 boolean isDeleteResourceVersionOk = true;
2131 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2132 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2134 if ((!currentResourceVersion.equals(resourceVersion))
2135 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2136 isDeleteResourceVersionOk = false;
2138 return isDeleteResourceVersionOk;
2142 * Convert from camel case.
2144 * @param name the name
2145 * @return the string
2147 private String convertFromCamelCase(String name) {
2149 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2151 NamingExceptions exceptions = NamingExceptions.getInstance();
2152 result = exceptions.getDBName(result);
2157 private boolean canModify(Introspector obj, String propName, String requestContext) {
2158 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2159 if (readOnly != null) {
2160 final String[] items = readOnly.split(",");
2161 for (String item : items) {
2162 if (requestContext.equals(item)) {
2170 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2172 SideEffectRunner runner = new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class)
2173 .addSideEffect(PrivateEdge.class).build();
2175 runner.execute(obj, self);
2178 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2180 SideEffectRunner runner =
2181 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2183 runner.execute(obj, self);
2186 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2188 SideEffectRunner runner =
2189 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
2191 runner.execute(obj, self);
2194 public double getDBTimeMsecs() {
2195 return (dbTimeMsecs);
2199 * Db to object With Filters
2200 * This is for a one-time run with Tenant Isloation to only filter relationships
2202 * @param obj the obj
2203 * @param v the vertex from the graph
2204 * @param depth the depth
2205 * @param nodeOnly specify if to exclude relationships or not
2206 * @param filterCousinNodes
2207 * @return the introspector
2208 * @throws AAIException the AAI exception
2209 * @throws IllegalAccessException the illegal access exception
2210 * @throws IllegalArgumentException the illegal argument exception
2211 * @throws InvocationTargetException the invocation target exception
2212 * @throws SecurityException the security exception
2213 * @throws InstantiationException the instantiation exception
2214 * @throws NoSuchMethodException the no such method exception
2215 * @throws UnsupportedEncodingException the unsupported encoding exception
2216 * @throws MalformedURLException the malformed URL exception
2217 * @throws AAIUnknownObjectException
2218 * @throws URISyntaxException
2220 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2221 List<String> filterCousinNodes, List<String> filterParentNodes)
2222 throws AAIException, UnsupportedEncodingException {
2223 return dbToObjectWithFilters(obj, v, seen, depth, nodeOnly,
2224 filterCousinNodes, filterParentNodes, false);
2228 * Db to object With Filters
2229 * This is for a one-time run with Tenant Isloation to only filter relationships
2230 * TODO: Chnage the original dbToObject to take filter parent/cousins
2232 * @param obj the obj
2233 * @param v the vertex from the graph
2234 * @param depth the depth
2235 * @param nodeOnly specify if to exclude relationships or not
2236 * @param filterCousinNodes
2237 * @param isSkipRelatedTo determine to incorporated related-to-property data
2238 * @return the introspector
2239 * @throws AAIException the AAI exception
2240 * @throws IllegalAccessException the illegal access exception
2241 * @throws IllegalArgumentException the illegal argument exception
2242 * @throws InvocationTargetException the invocation target exception
2243 * @throws SecurityException the security exception
2244 * @throws InstantiationException the instantiation exception
2245 * @throws NoSuchMethodException the no such method exception
2246 * @throws UnsupportedEncodingException the unsupported encoding exception
2247 * @throws MalformedURLException the malformed URL exception
2248 * @throws AAIUnknownObjectException
2249 * @throws URISyntaxException
2251 // TODO - See if you can merge the 2 dbToObjectWithFilters
2252 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2253 List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2254 throws AAIException, UnsupportedEncodingException {
2255 String cleanUp = FALSE;
2261 boolean modified = false;
2262 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2263 List<Object> getList = null;
2265 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2266 this.copySimpleProperty(property, obj, v);
2269 if (obj.isComplexType(property)) {
2270 /* container case */
2272 if (!property.equals("relationship-list") && depth >= 0) {
2273 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2274 Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly,
2275 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2276 if (result != null) {
2277 obj.setValue(property, argumentObject.getUnderlyingObject());
2280 } else if (property.equals("relationship-list") && !nodeOnly) {
2281 /* relationships need to be handled correctly */
2282 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2284 createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes, isSkipRelatedTo);
2285 if (relationshipList != null) {
2286 obj.setValue(property, relationshipList.getUnderlyingObject());
2291 } else if (obj.isListType(property)) {
2293 if (property.equals("any")) {
2296 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2297 if (obj.isComplexGenericType(property) && depth >= 0) {
2298 final String childDbName = convertFromCamelCase(genericType);
2299 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2302 boolean isThisParentRequired =
2303 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2305 EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
2308 rule = edgeRules.getRule(q);
2309 } catch (EdgeRuleNotFoundException e) {
2310 throw new NoEdgeRuleFoundException(e);
2311 } catch (AmbiguousRuleChoiceException e) {
2312 throw new MultipleEdgeRuleFoundException(e);
2314 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2315 Direction ruleDirection = rule.getDirection();
2316 List<Vertex> verticesList = new ArrayList<>();
2317 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2318 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2319 verticesList.add(vertex);
2322 if (!verticesList.isEmpty()) {
2323 getList = obj.getValue(property);
2326 for (Vertex childVertex : verticesList) {
2327 if (!seen.contains(childVertex)) {
2328 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2330 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2331 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2332 if (result != null) {
2333 getList.add(argumentObject.getUnderlyingObject());
2338 LOGGER.warn("Cycle found while serializing vertex id={}",
2339 childVertex.id().toString());
2342 if (processed == 0) {
2343 // vertices were all seen, reset the list
2346 if (processed > 0) {
2350 } else if (obj.isSimpleGenericType(property)) {
2351 List<Object> temp = this.engine.getListProperty(v, property);
2353 getList = obj.getValue(property);
2354 getList.addAll(temp);
2365 // no changes were made to this obj, discard the instance
2369 this.enrichData(obj, v);
2375 * Creates the relationship list with the filtered node types.
2378 * @param obj the obj
2379 * @param cleanUp the clean up
2380 * @return the object
2381 * @throws IllegalArgumentException the illegal argument exception
2382 * @throws SecurityException the security exception
2383 * @throws UnsupportedEncodingException the unsupported encoding exception
2384 * @throws AAIException the AAI exception
2386 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp,
2387 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2388 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2390 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2391 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2392 return filterNodes.parallelStream().anyMatch(node::contains);
2395 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2397 List<Vertex> cousins = new ArrayList<>();
2398 cousinVertices.forEachRemaining(cousins::add);
2399 for (Vertex cousin : cousins) {
2401 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2402 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2403 if (result != null) {
2404 relationshipObjList.add(result);
2409 if (relationshipObjList.isEmpty()) {
2416 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2417 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2418 return this.edgeVertexes;
2421 public void addVertexToEdgeVertexes(Vertex vertex){
2422 this.edgeVertexes.add(vertex);
2425 private String urlToUri(String url) {
2426 if (url.startsWith("/")) {
2427 url = url.substring(1);
2430 if (url.endsWith("/")) {
2431 url = url.substring(0, url.length() - 1);
2434 // TODO - Check if this makes to do for model driven for base uri path
2435 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2436 if (url.charAt(0) != '/') {