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 public DBSerializer(SchemaVersion version,
170 TransactionalGraphEngine engine,
171 ModelType introspectionType,
172 String sourceOfTruth,
173 int notificationDepth,
174 String serverBase) throws AAIException {
175 this.engine = engine;
176 this.sourceOfTruth = sourceOfTruth;
177 this.introspectionType = introspectionType;
178 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
179 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
181 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
182 this.version = version;
184 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
185 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
186 this.baseURL = serverBase;
187 this.currentTimeMillis = System.currentTimeMillis();
188 this.notificationDepth = notificationDepth;
192 private void initBeans() {
193 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
194 ApplicationContext ctx = SpringContextAware.getApplicationContext();
195 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
197 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
198 setEdgeSerializer(es);
199 isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
202 public void setEdgeSerializer(EdgeSerializer edgeSer) {
203 this.edgeSer = edgeSer;
206 public EdgeSerializer getEdgeSeriailizer() {
210 public void setEdgeIngestor(EdgeIngestor ei) {
214 public EdgeIngestor getEdgeIngestor() {
215 return this.edgeRules;
218 public Map<Vertex, Boolean> getUpdatedVertexes() {
219 return updatedVertexes;
222 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair(){
223 return impliedDeleteUriObjectPair;
227 * Touch standard vertex properties.
229 * @param isNewVertex the is new vertex
231 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
232 String timeNowInSec = Long.toString(currentTimeMillis);
234 String uuid = UUID.randomUUID().toString();
235 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
236 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
237 v.property(AAIProperties.AAI_UUID, uuid);
238 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
239 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
240 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
242 if(isDeltaEventsEnabled) {
243 standardVertexPropsDeltas(v, timeNowInSec);
245 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
246 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
247 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
251 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
252 String uri = v.property(AAIProperties.AAI_URI).value().toString();
253 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
254 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
255 if (getObjectDeltas().containsKey(uri)) {
256 getObjectDeltas().get(uri).setAction(objDeltaAction);
259 addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()), objDeltaAction);
260 addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()), objDeltaAction);
261 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()), objDeltaAction);
262 addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()), objDeltaAction);
264 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
267 AAIProperties.RESOURCE_VERSION,
268 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
273 AAIProperties.LAST_MOD_TS,
274 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
279 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
280 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
284 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
285 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
286 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
290 public Map<String, ObjectDelta> getObjectDeltas() {return objectDeltas;}
292 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
293 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
294 objectDelta.addPropertyDelta(prop, delta);
295 objectDeltas.put(uri, objectDelta);
298 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
299 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
300 objectDelta.addRelationshipDelta(delta);
301 objectDeltas.put(uri, objectDelta);
304 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
305 v.property(AAIProperties.NODE_TYPE, nodeType);
306 touchStandardVertexProperties(v, isNewVertex);
310 * Creates the new vertex.
312 * @param wrappedObject the wrapped object
315 public Vertex createNewVertex(Introspector wrappedObject) {
318 StopWatch.conditionalStart();
319 v = this.engine.tx().addVertex(wrappedObject.getDbName());
320 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
322 dbTimeMsecs += StopWatch.stopIfStarted();
330 * @param className the class name
334 * Removes the classpath from a class name
336 public String trimClassName(String className) {
337 String returnValue = "";
339 if (className.lastIndexOf('.') == -1) {
342 returnValue = className.substring(className.lastIndexOf('.') + 1);
352 * @param uriQuery the uri query
353 * @param identifier the identifier
354 * @throws SecurityException the security exception
355 * @throws IllegalArgumentException the illegal argument exception
356 * @throws AAIException the AAI exception
357 * @throws UnsupportedEncodingException the unsupported encoding exception
359 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
360 String requestContext) throws AAIException, UnsupportedEncodingException {
361 StopWatch.conditionalStart();
363 if (uriQuery.isDependent()) {
364 // try to find the parent
365 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
366 if (!vertices.isEmpty()) {
367 Vertex parent = vertices.get(0);
368 this.reflectDependentVertex(parent, v, obj, requestContext);
370 dbTimeMsecs += StopWatch.stopIfStarted();
371 throw new AAIException("AAI_6114",
372 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
375 serializeSingleVertex(v, obj, requestContext);
378 } catch (SchemaViolationException e) {
379 dbTimeMsecs += StopWatch.stopIfStarted();
380 throw new AAIException("AAI_6117", e);
382 dbTimeMsecs += StopWatch.stopIfStarted();
385 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
386 throws UnsupportedEncodingException, AAIException {
387 StopWatch.conditionalStart();
389 boolean isTopLevel = obj.isTopLevel();
391 addUriIfNeeded(v, obj.getURI());
394 URI uri = this.getURIForVertex(v);
395 URIParser parser = new URIParser(this.loader, uri);
396 if (parser.validate()) {
397 addUriIfNeeded(v, uri.toString());
400 processObject(obj, v, requestContext);
401 } catch (SchemaViolationException e) {
402 throw new AAIException("AAI_6117", e);
404 dbTimeMsecs += StopWatch.stopIfStarted();
408 private void addUriIfNeeded(Vertex v, String uri) {
409 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
410 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
411 v.property(AAIProperties.AAI_URI, uri);
421 * @throws IllegalArgumentException the illegal argument exception
422 * @throws SecurityException the security exception
423 * @throws AAIException the AAI exception
424 * @throws UnsupportedEncodingException the unsupported encoding exception
427 * Helper method for reflectToDb
428 * Handles all the property setting
430 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
431 throws UnsupportedEncodingException, AAIException {
432 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
433 properties.remove(AAIProperties.RESOURCE_VERSION);
434 List<Vertex> dependentVertexes = new ArrayList<>();
435 List<Vertex> processedVertexes = new ArrayList<>();
437 boolean isComplexType ;
440 // If the notification depth is set to maximum
441 // this is the behavior of the expected clients
442 if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
443 if (!obj.isContainer()) {
444 this.touchStandardVertexProperties(v, false);
447 this.executePreSideEffects(obj, v);
448 for (String property : properties) {
449 final String propertyType;
450 propertyType = obj.getType(property);
451 isComplexType = obj.isComplexType(property);
452 isListType = obj.isListType(property);
453 Object value = obj.getValue(property);
455 if (!(isComplexType || isListType)) {
456 boolean canModify = this.canModify(obj, property, requestContext);
459 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
460 String dbProperty = property;
461 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
462 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
464 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
465 // data linked properties are ephemeral
466 // they are populated dynamically on GETs
469 Object oldValue = v.property(dbProperty).orElse(null);
470 String uri = getURIForVertex(v).toString();
472 if (!value.equals(oldValue)) {
473 if (propertyType.toLowerCase().contains(".long")) {
474 v.property(dbProperty, new Integer(((Long) value).toString()));
476 v.property(dbProperty, value);
478 if (isDeltaEventsEnabled) {
479 createDeltaProperty(uri, value, dbProperty, oldValue);
481 this.updatedVertexes.putIfAbsent(v, false);
484 if (oldValue != null) {
485 v.property(dbProperty).remove();
486 if (isDeltaEventsEnabled) {
487 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
489 this.updatedVertexes.putIfAbsent(v, false);
493 } else if (isListType) {
494 List<Object> list = (List<Object>) value;
495 if (obj.isComplexGenericType(property)) {
497 for (Object o : list) {
498 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
499 child.setURIChain(obj.getURI());
500 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
505 if (isDeltaEventsEnabled) {
506 String uri = getURIForVertex(v).toString();
507 List<Object> oldVal = engine.getListProperty(v, property);
508 engine.setListProperty(v, property, list);
509 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
510 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
511 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
513 } else { // is either a create or update and is handled by the called method
514 createDeltaProperty(uri, list, property, oldVal);
517 engine.setListProperty(v, property, list);
519 this.updatedVertexes.putIfAbsent(v, false);
522 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
523 // back to this method
524 if (value != null) { // effectively ignore complex properties not included in the object we're
526 if (value.getClass().isArray()) {
528 int length = Array.getLength(value);
529 for (int i = 0; i < length; i++) {
530 Object arrayElement = Array.get(value, i);
531 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
532 child.setURIChain(obj.getURI());
533 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
536 } else if (!property.equals("relationship-list")) {
538 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
539 if (introspector.isContainer()) {
540 dependentVertexes.addAll(
541 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
542 introspector.setURIChain(obj.getURI());
544 processedVertexes.addAll(processObject(introspector, v, requestContext));
547 dependentVertexes.addAll(
548 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
549 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
552 } else if (property.equals("relationship-list")) {
553 handleRelationships(obj, v);
558 this.writeThroughDefaults(v, obj);
559 /* handle those vertexes not touched */
560 for (Vertex toBeKept : processedVertexes) {
561 dependentVertexes.remove(toBeKept);
564 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
565 List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
567 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
568 for(Vertex curVertex : impliedDeleteVertices){
569 if(!curVertex.property("aai-uri").isPresent()){
570 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
573 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
574 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
576 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
578 if(!curObj.isTopLevel()){
579 curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
582 if(!impliedDeleteUriObjectPair.containsKey(curAaiUri)){
583 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
588 impliedDelete.delete(impliedDeleteVertices);
590 // touch svp using vertex list for what changed
591 // if the notification depth is zero
592 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
593 this.updatedVertexes.entrySet().stream()
594 .filter(e -> !e.getValue())
595 .filter(e -> !edgeVertexes.contains(e.getKey()))
597 this.touchStandardVertexProperties(e.getKey(), false);
601 this.executePostSideEffects(obj, v);
602 return processedVertexes;
605 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
606 if (oldValue == null) {
607 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
609 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
613 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
614 Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
616 HashMap<String, Introspector> relatedVertices = new HashMap<>();
617 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
619 if (!aaiUriProperty.isPresent()) {
620 if (LOGGER.isDebugEnabled()) {
621 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
625 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
627 return relatedVertices;
630 String aaiUri = aaiUriProperty.value().toString();
632 if (!obj.isTopLevel()) {
633 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
634 List<Vertex> vertexChain;
635 // If the uriList is null then there is something wrong with converting the uri
636 // into a list of aai-uris so falling back to the old mechanism for finding parents
637 if (uriList == null) {
639 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
640 vertexChain = queryEngine.findParents(v);
641 } else if (uriList.length == 1) {
642 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
643 vertexChain = Collections.singletonList(v);
645 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
646 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
647 // inject v into start of vertexChain
648 vertexChain.add(0, v);
650 for (Vertex vertex : vertexChain) {
652 final Introspector vertexObj = this.getVertexProperties(vertex);
653 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
654 } catch (AAIUnknownObjectException e) {
655 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
660 final Introspector vertexObj = this.getVertexProperties(v);
661 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
662 } catch (AAIUnknownObjectException e) {
663 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
667 return relatedVertices;
671 * Given an uri, introspector object and loader object
672 * it will check if the obj is top level object if it is,
673 * it will return immediately returning the uri passed in
674 * If it isn't, it will go through, get the uriTemplate
675 * from the introspector object and get the count of "/"s
676 * and remove that part of the uri using substring
677 * and keep doing that until the current object is top level
678 * Also added the max depth just so worst case scenario
679 * Then keep adding aai-uri to the list include the aai-uri passed in
680 * Convert that list into an array and return it
687 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
689 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
690 * it converts to /vservers/vserver
692 * lastIndexOf /vservers/vserver in
693 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
698 * Use substring to get the string from 0 to that lastIndexOf
699 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
701 * From this new aai-uri, generate a introspector from the URITOObject class
702 * and keep doing this until you
706 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
707 * @param obj - introspector object of the given starting vertex
708 * @param loader - Type of loader which will always be MoxyLoader to support model driven
709 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
710 * @throws UnsupportedEncodingException
711 * @throws AAIException
713 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
714 throws UnsupportedEncodingException, AAIException {
716 List<String> uriList = new ArrayList<>();
718 String truncatedUri = aaiUri;
719 int depth = AAIProperties.MAXIMUM_DEPTH;
720 uriList.add(truncatedUri);
722 while (depth >= 0 && !obj.isTopLevel()) {
723 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
725 if (template == null) {
726 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
730 int templateCount = StringUtils.countMatches(template, "/");
731 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
733 if (templateCount > truncatedUriCount) {
734 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
738 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
739 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
740 uriList.add(truncatedUri);
741 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
745 return uriList.toArray(new String[0]);
749 * Handle relationships.
752 * @param vertex the vertex
753 * @throws SecurityException the security exception
754 * @throws IllegalArgumentException the illegal argument exception
755 * @throws UnsupportedEncodingException the unsupported encoding exception
756 * @throws AAIException the AAI exception
759 * Handles the explicit relationships defined for an obj
761 private void handleRelationships(Introspector obj, Vertex vertex)
762 throws UnsupportedEncodingException, AAIException {
764 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
765 processRelationshipList(wrappedRl, vertex);
770 * Process relationship list.
772 * @param wrapped the wrapped
774 * @throws UnsupportedEncodingException the unsupported encoding exception
775 * @throws AAIException the AAI exception
777 private void processRelationshipList(Introspector wrapped, Vertex v)
778 throws UnsupportedEncodingException, AAIException {
780 List<Object> relationships = wrapped.getValue("relationship");
781 String mainUri = getURIForVertex(v).toString();
782 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
783 EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
784 .edgeType(EdgeType.COUSIN)
785 .version(wrapped.getVersion());
786 EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
787 .edgeType(EdgeType.TREE)
788 .version(wrapped.getVersion());
790 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
792 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
793 for (Object relationship : relationships) {
796 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
797 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
799 if (relUri.startsWith("/vnf/")) {
800 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
801 List<Vertex> results = parser.getQueryBuilder().toList();
802 if (results.isEmpty()) {
803 final AAIException ex = new AAIException(AAI_6129,
804 String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
805 ex.getTemplateVars().add(parser.getResultType());
806 ex.getTemplateVars().add(parser.getUri().toString());
809 // still an issue if there's more than one
810 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
811 relUri = results.get(0).value(AAIProperties.AAI_URI);
813 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
819 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
820 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
822 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
823 String bNodeType = uriToObject.getEntityName();
824 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
825 if (!edgeIngestor.hasRule(ruleQuery)) {
826 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
827 if (edgeIngestor.hasRule(treeQuery)) {
828 throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
830 throw new AAIException("AAI_6120", String.format(
831 "No EdgeRule found for passed nodeTypes: %s, %s.",
832 aNodeType, bNodeType));
835 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
836 if (rules.size() == 1) {
837 label = rules.get(0).getLabel();
839 Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
840 if (defaultRule.isPresent()) {
841 label = defaultRule.get().getLabel();
843 throw new AAIException(AAI_6145);
846 } catch (EdgeRuleNotFoundException ea) {
847 throw new AAIException(AAI_6145, ea);
851 cousinUriAndLabels.add(Pair.with(relUri, label));
854 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
855 Set<Path> toRemove = new HashSet<>();
858 // for each path 3 things can happen:
859 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
860 // 2. The edge rule exits in this version it's included in the request the edge is left alone
861 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
862 for (Path path : paths) {
863 if (path.size() < 3) {
868 // v ----related-to--> otherV
869 // In the above case,
870 // path objects get(0) returns vertex v
871 // path objects.get(1) returns edge related-to
872 // path objects.get(2) returns vertex otherV
873 Vertex otherV = path.get(2);
876 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
877 bUri = otherV.value(AAIProperties.AAI_URI);
881 String edgeLabel = path.<Edge>get(1).label();
883 Pair<String, String> key = Pair.with(bUri, edgeLabel);
884 if (cousinUriAndLabels.contains(key)) {
885 cousinUriAndLabels.remove(key);
888 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
889 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
893 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
894 if (edgeIngestor.hasRule(ruleQuery)) {
901 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
902 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
905 String label = cousinUriAndLabel.getValue1();
906 String cousinUri = cousinUriAndLabel.getValue0();
907 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
909 List<Vertex> results = parser.getQueryBuilder().toList();
910 if (results.isEmpty()) {
911 final AAIException ex = new AAIException(AAI_6129,
912 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
913 ex.getTemplateVars().add(parser.getResultType());
914 ex.getTemplateVars().add(parser.getUri().toString());
917 // still an issue if there's more than one
918 cousinVertex = results.get(0);
921 if (cousinVertex != null) {
922 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
923 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
924 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
926 if (!edgeRules.hasRule(baseQ.build())) {
927 throw new AAIException("AAI_6120", String.format(
928 "No EdgeRule found for passed nodeTypes: %s, %s%s.",
929 aNodeType, cousinType, label != null ? (" with label " + label) : ""));
930 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
931 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
932 throw new AAIException(AAI_6145);
935 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
938 toBeCreated.add(Pair.with(cousinVertex, label));
943 for (Path path : toRemove) {
944 if(isDeltaEventsEnabled) {
945 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
947 this.updatedVertexes.putIfAbsent(v, false);
948 this.edgeVertexes.add(path.get(2));
949 path.<Edge>get(1).remove();
952 for (Pair<Vertex, String> create : toBeCreated) {
954 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
955 if (isDeltaEventsEnabled) {
956 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
958 this.updatedVertexes.putIfAbsent(v, false);
959 this.edgeVertexes.add(create.getValue0());
960 } catch (NoEdgeRuleFoundException ex) {
961 throw new AAIException(AAI_6129, ex);
967 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
968 RelationshipDelta relationshipDelta = new RelationshipDelta(
970 edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
971 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
972 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
973 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
975 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
976 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
980 * Write through defaults.
984 * @throws AAIUnknownObjectException
986 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
987 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
988 if (latest != null) {
989 Set<String> required = latest.getRequiredProperties();
991 for (String field : required) {
992 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
993 if (defaultValue != null) {
994 Object vertexProp = v.property(field).orElse(null);
995 if (vertexProp == null) {
996 v.property(field, defaultValue);
1005 * Reflect dependent vertex.
1008 * @param dependentObj the dependent obj
1009 * @return the vertex
1010 * @throws IllegalArgumentException the illegal argument exception
1011 * @throws SecurityException the security exception
1012 * @throws AAIException the AAI exception
1013 * @throws UnsupportedEncodingException the unsupported encoding exception
1014 * @throws AAIUnknownObjectException
1016 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1017 throws AAIException, UnsupportedEncodingException {
1019 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1020 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1021 query.createKeyQuery(dependentObj);
1023 List<Vertex> items = query.toList();
1025 Vertex dependentVertex;
1026 if (items.size() == 1) {
1027 dependentVertex = items.get(0);
1028 this.verifyResourceVersion("update", dependentObj.getDbName(),
1029 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1030 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1032 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1033 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1034 dependentVertex = createNewVertex(dependentObj);
1037 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1042 * Reflect dependent vertex.
1044 * @param parent the parent
1045 * @param child the child
1046 * @param obj the obj
1047 * @return the vertex
1048 * @throws IllegalArgumentException the illegal argument exception
1049 * @throws SecurityException the security exception
1050 * @throws AAIException the AAI exception
1051 * @throws UnsupportedEncodingException the unsupported encoding exception
1052 * @throws AAIUnknownObjectException
1054 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1055 throws AAIException, UnsupportedEncodingException {
1057 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1058 if (parentUri != null) {
1061 addUriIfNeeded(child, parentUri + uri);
1063 processObject(obj, child, requestContext);
1066 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1069 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1070 if (canBeLinked != null && canBeLinked.equals("true")) {
1071 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1072 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1073 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1074 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1076 child.property(AAIProperties.LINKED, true);
1079 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1080 if(isDeltaEventsEnabled) {
1081 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1088 private SchemaVersion getVerForContext(String requestContext) {
1089 Pattern pattern = Pattern.compile("v[0-9]+");
1090 Matcher m = pattern.matcher(requestContext);
1092 return this.version;
1094 return new SchemaVersion(requestContext);
1101 * @param vertices the vertices
1102 * @param obj the obj
1103 * @param depth the depth
1104 * @param cleanUp the clean up
1105 * @return the introspector
1106 * @throws AAIException the AAI exception
1107 * @throws IllegalArgumentException the illegal argument exception
1108 * @throws SecurityException the security exception
1109 * @throws UnsupportedEncodingException the unsupported encoding exception
1110 * @throws AAIUnknownObjectException
1111 * @throws URISyntaxException
1113 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1114 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1115 final int internalDepth;
1116 if (depth == Integer.MAX_VALUE) {
1117 internalDepth = depth--;
1119 internalDepth = depth;
1121 StopWatch.conditionalStart();
1122 if (vertices.size() > 1 && !obj.isContainer()) {
1123 dbTimeMsecs += StopWatch.stopIfStarted();
1124 throw new AAIException("AAI_6136",
1125 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1126 } else if (obj.isContainer()) {
1128 String listProperty = null;
1129 for (String property : obj.getProperties()) {
1130 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1131 listProperty = property;
1135 final String propertyName = listProperty;
1136 getList = obj.getValue(listProperty);
1139 * This is an experimental multithreading experiment
1142 ExecutorService pool = GetAllPool.getInstance().getPool();
1144 List<Future<Object>> futures = new ArrayList<>();
1146 for (Vertex v : vertices) {
1147 AaiCallable<Object> task = new AaiCallable<Object>() {
1149 public Object process() throws UnsupportedEncodingException, AAIException {
1150 Set<Vertex> seen = new HashSet<>();
1151 Introspector childObject;
1152 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1153 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1154 return childObject.getUnderlyingObject();
1157 futures.add(pool.submit(task));
1160 for (Future<Object> future : futures) {
1162 getList.add(future.get());
1163 } catch (ExecutionException | InterruptedException e) {
1164 dbTimeMsecs += StopWatch.stopIfStarted();
1165 throw new AAIException("AAI_4000", e);
1168 } else if (vertices.size() == 1) {
1169 Set<Vertex> seen = new HashSet<>();
1170 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1175 dbTimeMsecs += StopWatch.stopIfStarted();
1182 * @param vertices the vertices
1183 * @param obj the obj
1184 * @param depth the depth
1185 * @param cleanUp the clean up
1186 * @return the introspector
1187 * @throws AAIException the AAI exception
1188 * @throws IllegalArgumentException the illegal argument exception
1189 * @throws SecurityException the security exception
1190 * @throws UnsupportedEncodingException the unsupported encoding exception
1191 * @throws AAIUnknownObjectException
1192 * @throws URISyntaxException
1194 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1195 String cleanUp) throws UnsupportedEncodingException, AAIException {
1196 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1202 * @param obj the obj
1204 * @param seen the seen
1205 * @param depth the depth
1206 * @param cleanUp the clean up
1207 * @return the introspector
1208 * @throws IllegalArgumentException the illegal argument exception
1209 * @throws SecurityException the security exception
1210 * @throws UnsupportedEncodingException the unsupported encoding exception
1211 * @throws AAIException the AAI exception
1212 * @throws AAIUnknownObjectException
1213 * @throws URISyntaxException
1215 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1216 String cleanUp) throws AAIException, UnsupportedEncodingException {
1217 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1223 * @param obj the obj
1225 * @param seen the seen
1226 * @param depth the depth
1227 * @param cleanUp the clean up
1228 * @return the introspector
1229 * @throws IllegalArgumentException the illegal argument exception
1230 * @throws SecurityException the security exception
1231 * @throws UnsupportedEncodingException the unsupported encoding exception
1232 * @throws AAIException the AAI exception
1233 * @throws AAIUnknownObjectException
1234 * @throws URISyntaxException
1236 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1237 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1245 boolean modified = false;
1246 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1247 List<Object> getList = null;
1249 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1250 this.copySimpleProperty(property, obj, v);
1253 if (obj.isComplexType(property)) {
1254 /* container case */
1256 if (!property.equals("relationship-list") && depth >= 0) {
1257 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1258 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1259 if (result != null) {
1260 obj.setValue(property, argumentObject.getUnderlyingObject());
1263 } else if (property.equals("relationship-list") && !nodeOnly) {
1264 /* relationships need to be handled correctly */
1265 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1266 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1267 if (relationshipList != null) {
1268 obj.setValue(property, relationshipList.getUnderlyingObject());
1273 } else if (obj.isListType(property)) {
1275 if (property.equals("any")) {
1278 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1279 if (obj.isComplexGenericType(property) && depth >= 0) {
1280 final String childDbName = convertFromCamelCase(genericType);
1281 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1285 rule = edgeRules.getRule(
1286 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1287 } catch (EdgeRuleNotFoundException e) {
1288 throw new NoEdgeRuleFoundException(e);
1289 } catch (AmbiguousRuleChoiceException e) {
1290 throw new MultipleEdgeRuleFoundException(e);
1292 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1294 Direction ruleDirection = rule.getDirection();
1295 List<Vertex> verticesList = new ArrayList<>();
1296 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1297 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1298 verticesList.add(vertex);
1301 if (!verticesList.isEmpty()) {
1302 getList = obj.getValue(property);
1305 for (Vertex childVertex : verticesList) {
1306 if (!seen.contains(childVertex)) {
1307 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1310 dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1311 if (result != null) {
1312 getList.add(argumentObject.getUnderlyingObject());
1317 LOGGER.warn("Cycle found while serializing vertex id={}",
1318 childVertex.id().toString());
1321 if (processed == 0) {
1322 // vertices were all seen, reset the list
1325 if (processed > 0) {
1329 } else if (obj.isSimpleGenericType(property)) {
1330 List<Object> temp = this.engine.getListProperty(v, property);
1332 getList = (List<Object>) obj.getValue(property);
1333 getList.addAll(temp);
1341 // no changes were made to this obj, discard the instance
1345 this.enrichData(obj, v);
1350 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1351 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1352 if (nodeType == null) {
1353 throw new AAIException("AAI_6143");
1356 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1357 Set<Vertex> seen = new HashSet<>();
1359 StopWatch.conditionalStart();
1360 this.dbToObject(obj, v, seen, depth, true, FALSE);
1361 dbTimeMsecs += StopWatch.stopIfStarted();
1366 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1367 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1370 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1371 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1372 if (nodeType == null) {
1373 throw new AAIException("AAI_6143");
1375 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1376 Set<Vertex> seen = new HashSet<>();
1377 StopWatch.conditionalStart();
1378 this.dbToObject(obj, v, seen, depth, false, FALSE);
1379 dbTimeMsecs += StopWatch.stopIfStarted();
1384 * Copy simple property.
1386 * @param property the property
1387 * @param obj the obj
1389 * @throws IllegalArgumentException the illegal argument exception
1390 * @throws SecurityException the security exception
1392 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1393 final Object temp = getProperty(obj, property, v);
1395 obj.setValue(property, temp);
1399 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1401 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1402 String[] simplePropsArray = new String[simpleProperties.size()];
1403 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1405 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1407 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1409 return simplePropsHashMap;
1412 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1413 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1414 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1415 return relationshipList;
1419 * Creates the relationship list.
1422 * @param obj the obj
1423 * @param cleanUp the clean up
1424 * @return the object
1425 * @throws IllegalArgumentException the illegal argument exception
1426 * @throws SecurityException the security exception
1427 * @throws UnsupportedEncodingException the unsupported encoding exception
1428 * @throws AAIException the AAI exception
1429 * @throws URISyntaxException
1431 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1432 throws UnsupportedEncodingException, AAIException {
1433 // default boolean value for isSkipRelatedTo is false
1434 return createRelationshipList(v, obj, cleanUp, false);
1438 * Creates the relationship list.
1441 * @param obj the obj
1442 * @param cleanUp the clean up
1443 * @param isSkipRelatedTo to determine adding related-to-property in response
1444 * @return the object
1445 * @throws IllegalArgumentException the illegal argument exception
1446 * @throws SecurityException the security exception
1447 * @throws UnsupportedEncodingException the unsupported encoding exception
1448 * @throws AAIException the AAI exception
1449 * @throws URISyntaxException
1451 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1452 throws UnsupportedEncodingException, AAIException {
1454 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1455 VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1457 if (!nodeTypeProperty.isPresent()) {
1458 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1462 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1464 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1466 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1468 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1469 .edgeType(EdgeType.COUSIN)
1470 .version(obj.getVersion());
1472 for (Path path : paths){
1473 if(path.size() < 3){
1478 // v ----related-to--> otherV
1479 // In the above case,
1480 // path objects get(0) returns vertex v
1481 // path objects.get(1) returns edge related-to
1482 // path objects.get(2) returns vertex otherV
1483 Edge edge = path.get(1);
1484 Vertex otherV= path.get(2);
1486 // TODO: Come back and revisit this code
1487 // Create a query based on the a nodetype and b nodetype
1488 // which is also a cousin edge and ensure the version
1489 // is used properly so for example in order to be backwards
1490 // compatible if we had allowed a edge between a and b
1491 // in a previous release and we decided to remove it from
1492 // the edge rules in the future we can display the edge
1493 // only for the older apis and the new apis if the edge rule
1494 // is removed will not be seen in the newer version of the API
1497 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1498 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1503 String edgeLabel = edge.label();
1504 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1506 if(!edgeIngestor.hasRule(ruleQuery)){
1507 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1511 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1512 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1513 if (result != null) {
1514 relationshipObjList.add(result);
1519 if (relationshipObjList.isEmpty()) {
1527 * Process edge relationship.
1529 * @param relationshipObj the relationship obj
1530 * @param edgeLabel the edge's label
1531 * @param cleanUp the clean up
1532 * @return the object
1533 * @throws IllegalArgumentException the illegal argument exception
1534 * @throws SecurityException the security exception
1535 * @throws UnsupportedEncodingException the unsupported encoding exception
1536 * @throws AAIUnknownObjectException
1537 * @throws URISyntaxException
1539 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1540 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1542 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1544 if (!aaiUriProperty.isPresent()) {
1548 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1550 URIToRelationshipObject uriParser;
1551 Introspector result;
1553 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1554 result = uriParser.getResult();
1555 } catch (AAIException | URISyntaxException e) {
1556 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1557 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1561 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1563 if (cousinVertexNodeType.isPresent()) {
1564 String cousinType = cousinVertexNodeType.value().toString();
1565 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1566 this.addRelatedToProperty(result, cousin, cousinType);
1570 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1571 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1574 return result.getUnderlyingObject();
1578 * Gets the URI for vertex.
1581 * @return the URI for vertex
1582 * @throws IllegalArgumentException the illegal argument exception
1583 * @throws SecurityException the security exception
1584 * @throws UnsupportedEncodingException the unsupported encoding exception
1585 * @throws AAIUnknownObjectException
1587 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1589 return getURIForVertex(v, false);
1592 public URI getURIForVertex(Vertex v, boolean overwrite) {
1593 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1595 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1597 if (aaiUri != null && !overwrite) {
1598 uri = UriBuilder.fromPath(aaiUri).build();
1605 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1606 throws AAIUnknownObjectException {
1611 obj = this.loader.introspectorFromName(cousinType);
1612 } catch (AAIUnknownObjectException ex) {
1613 if (LOGGER.isTraceEnabled()) {
1614 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1615 cousinType, cousinVertex.id());
1620 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1621 List<Introspector> relatedToProperties = new ArrayList<>();
1623 if (nameProps != null) {
1624 String[] props = nameProps.split(",");
1625 for (String prop : props) {
1626 final Object temp = getProperty(obj, prop, cousinVertex);
1627 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1628 relatedTo.setValue("property-key", cousinType + "." + prop);
1629 relatedTo.setValue("property-value", temp);
1630 relatedToProperties.add(relatedTo);
1634 if (!relatedToProperties.isEmpty()) {
1635 List<Object> relatedToList = relationship.getValue("related-to-property");
1636 for (Introspector introspector : relatedToProperties) {
1637 relatedToList.add(introspector.getUnderlyingObject());
1643 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1645 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1646 String dbPropertyName = prop;
1648 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1649 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1652 return vertex.<Object>property(dbPropertyName).orElse(null);
1658 * @param relationship the relationship
1659 * @param inputVertex the input vertex
1660 * @return true, if successful
1661 * @throws UnsupportedEncodingException the unsupported encoding exception
1662 * @throws AAIException the AAI exception
1664 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1665 throws UnsupportedEncodingException, AAIException {
1667 Vertex relatedVertex;
1668 StopWatch.conditionalStart();
1669 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1671 String label = null;
1672 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1673 label = relationship.getValue(RELATIONSHIP_LABEL);
1676 List<Vertex> results = parser.getQueryBuilder().toList();
1677 if (results.isEmpty()) {
1678 dbTimeMsecs += StopWatch.stopIfStarted();
1679 AAIException e = new AAIException(AAI_6129,
1680 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1681 e.getTemplateVars().add(parser.getResultType());
1682 e.getTemplateVars().add(parser.getUri().toString());
1685 // still an issue if there's more than one
1686 relatedVertex = results.get(0);
1689 if (relatedVertex != null) {
1693 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1695 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1696 if(isDeltaEventsEnabled) {
1697 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1700 // attempted to link two vertexes already linked
1703 dbTimeMsecs += StopWatch.stopIfStarted();
1707 dbTimeMsecs += StopWatch.stopIfStarted();
1708 return relatedVertex;
1712 * Gets all the edges between of the type with the specified label.
1714 * @param aVertex the out vertex
1715 * @param bVertex the in vertex
1716 * @return the edges between
1718 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1722 if (bVertex != null) {
1723 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1724 if (EdgeType.TREE.equals(type)) {
1725 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1726 if (edgeRule.getDirection().equals(Direction.IN)) {
1727 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1728 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1729 .not(__.has(EdgeField.PRIVATE.toString(), true));
1731 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1732 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1733 .not(__.has(EdgeField.PRIVATE.toString(), true));
1735 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1737 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1738 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1739 .not(__.has(EdgeField.PRIVATE.toString(), true));
1740 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1742 List<Edge> list = findEdgesBetween.toList();
1743 if (!list.isEmpty()) {
1744 result = list.get(0);
1752 * Gets all the edges string between of the type.
1754 * @param aVertex the out vertex
1755 * @param bVertex the in vertex
1756 * @return the edges between
1757 * @throws NoEdgeRuleFoundException
1759 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1761 List<String> result = new ArrayList<>();
1763 if (bVertex != null) {
1764 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1765 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1766 if (EdgeType.TREE.equals(type)) {
1767 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1768 __.has(EdgeField.PRIVATE.toString(), true)));
1770 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1771 .not(__.has(EdgeField.PRIVATE.toString(), true));
1773 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1774 result = findEdgesBetween.label().toList();
1780 * Gets all the edges between the vertexes with the label and type.
1782 * @param aVertex the out vertex
1783 * @param bVertex the in vertex
1785 * @return the edges between
1786 * @throws AAIException the AAI exception
1788 private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1792 if (bVertex != null) {
1793 String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1794 String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1795 EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1798 rule = edgeRules.getRule(q);
1799 } catch (EdgeRuleNotFoundException e) {
1800 throw new NoEdgeRuleFoundException(e);
1801 } catch (AmbiguousRuleChoiceException e) {
1802 throw new MultipleEdgeRuleFoundException(e);
1804 edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1811 * Gets the edge between with the label and edge type.
1813 * @param aVertex the out vertex
1814 * @param bVertex the in vertex
1816 * @return the edge between
1817 * @throws AAIException the AAI exception
1818 * @throws NoEdgeRuleFoundException
1820 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1822 StopWatch.conditionalStart();
1823 if (bVertex != null) {
1825 Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1827 dbTimeMsecs += StopWatch.stopIfStarted();
1832 dbTimeMsecs += StopWatch.stopIfStarted();
1836 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1837 return this.getEdgeBetween(type, aVertex, bVertex, null);
1843 * @param relationship the relationship
1844 * @param inputVertex the input vertex
1845 * @return true, if successful
1846 * @throws UnsupportedEncodingException the unsupported encoding exception
1847 * @throws AAIException the AAI exception
1849 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1850 throws UnsupportedEncodingException, AAIException {
1852 Vertex relatedVertex;
1853 StopWatch.conditionalStart();
1854 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1856 List<Vertex> results = parser.getQueryBuilder().toList();
1858 String label = null;
1859 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1860 label = relationship.getValue(RELATIONSHIP_LABEL);
1863 if (results.isEmpty()) {
1864 dbTimeMsecs += StopWatch.stopIfStarted();
1865 return Optional.empty();
1868 relatedVertex = results.get(0);
1871 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1872 } catch (NoEdgeRuleFoundException e) {
1873 dbTimeMsecs += StopWatch.stopIfStarted();
1874 throw new AAIException(AAI_6129, e);
1877 if(isDeltaEventsEnabled) {
1878 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1879 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1882 dbTimeMsecs += StopWatch.stopIfStarted();
1883 return Optional.of(relatedVertex);
1885 dbTimeMsecs += StopWatch.stopIfStarted();
1886 return Optional.empty();
1892 * Delete with traversal.
1894 * @param startVertex the start vertex
1896 public void deleteWithTraversal(Vertex startVertex) {
1897 StopWatch.conditionalStart();
1898 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1899 this.delete(results);
1903 * Removes the list of vertexes from the graph
1905 * Current the vertex label will just be vertex but
1906 * in the future the aai-node-type property will be replaced
1907 * by using the vertex label as when retrieving an vertex
1908 * and retrieving an single property on an vertex will pre-fetch
1909 * all the properties of that vertex and this is due to the following property
1912 * query.fast-property=true
1915 * JanusGraph doesn't provide the capability to override that
1916 * at a transaction level and there is a plan to move to vertex label
1917 * so it is best to utilize this for now and when the change is applied
1919 * @param vertices - list of vertices to delete from the graph
1921 void delete(List<Vertex> vertices) {
1922 StopWatch.conditionalStart();
1924 for (Vertex v : vertices) {
1925 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
1926 if(isDeltaEventsEnabled) {
1927 deltaForVertexDelete(v);
1929 //add the cousin vertexes of v to have their resource-version updated and notified on.
1930 v.edges(Direction.BOTH)
1931 .forEachRemaining(e -> {
1932 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
1933 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
1934 e.bothVertices().forEachRemaining(cousinV -> {
1935 if (!v.equals(cousinV)) {
1936 edgeVertexes.add(cousinV);
1942 //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
1943 // it should be removed from them as v is to be deleted
1944 edgeVertexes.remove(v);
1945 updatedVertexes.remove(v);
1949 dbTimeMsecs += StopWatch.stopIfStarted();
1952 private void deltaForVertexDelete(Vertex v) {
1953 String aaiUri = v.property(AAIProperties.AAI_URI).value().toString();
1954 v.keys().forEach(k -> {
1955 List<Object> list = new ArrayList<>();
1956 v.properties(k).forEachRemaining(vp -> list.add(vp.value()));
1957 if (list.size() == 1) {
1958 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)), DeltaAction.DELETE);
1960 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE);
1964 v.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
1971 * @param resourceVersion the resource version
1972 * @throws IllegalArgumentException the illegal argument exception
1973 * @throws AAIException the AAI exception
1974 * @throws InterruptedException the interrupted exception
1976 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
1977 throws IllegalArgumentException, AAIException {
1979 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1981 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1982 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
1983 * So its better to make these in 2 steps
1985 if (result && !deletableVertices.isEmpty()) {
1986 result = verifyPreventDeleteSemantics(deletableVertices);
1991 deleteWithTraversal(v);
1992 } catch (IllegalStateException e) {
1993 throw new AAIException("AAI_6110", e);
2003 * @param resourceVersion the resource version
2004 * @throws IllegalArgumentException the illegal argument exception
2005 * @throws AAIException the AAI exception
2006 * @throws InterruptedException the interrupted exception
2008 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion)
2009 throws IllegalArgumentException, AAIException {
2011 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2016 deleteWithTraversal(v);
2017 } catch (IllegalStateException e) {
2018 throw new AAIException("AAI_6110", e);
2025 * Verify delete semantics.
2027 * @param vertex the vertex
2028 * @param resourceVersion the resource version
2029 * @return true, if successful
2030 * @throws AAIException the AAI exception
2032 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2033 throws AAIException {
2036 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2037 if (enableResourceVersion) {
2038 this.verifyResourceVersion("delete", nodeType,
2039 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2041 List<Vertex> vertices = new ArrayList<>();
2042 vertices.add(vertex);
2043 result = verifyPreventDeleteSemantics(vertices);
2049 * Verify Prevent delete semantics.
2051 * @param vertices the list of vertices
2052 * @return true, if successful
2053 * @throws AAIException the AAI exception
2055 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2056 boolean result = true;
2057 String errorDetail = " unknown delete semantic found";
2058 String aaiExceptionCode = "";
2060 StopWatch.conditionalStart();
2062 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2063 * "prevent-delete" condition
2064 * If yes - that should prevent the deletion of the vertex
2065 * Dedup makes sure we dont capture the prevent-delete vertices twice
2066 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2069 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2070 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2071 .values(AAIProperties.NODE_TYPE),
2072 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2073 .values(AAIProperties.NODE_TYPE))
2076 dbTimeMsecs += StopWatch.stopIfStarted();
2077 if (!preventDeleteVertices.isEmpty()) {
2078 aaiExceptionCode = "AAI_6110";
2079 errorDetail = String.format(
2080 "Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s",
2081 preventDeleteVertices);
2085 throw new AAIException(aaiExceptionCode, errorDetail);
2091 * Verify resource version.
2093 * @param action the action
2094 * @param nodeType the node type
2095 * @param currentResourceVersion the current resource version
2096 * @param resourceVersion the resource version
2097 * @param uri the uri
2098 * @return true, if successful
2099 * @throws AAIException the AAI exception
2101 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2102 String resourceVersion, String uri) throws AAIException {
2103 String enabled = "";
2104 String errorDetail = "";
2105 String aaiExceptionCode = "";
2106 boolean isDeleteResourceVersionOk = true;
2107 if (currentResourceVersion == null) {
2108 currentResourceVersion = "";
2111 if (resourceVersion == null) {
2112 resourceVersion = "";
2115 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2117 } catch (AAIException e) {
2118 ErrorLogHelper.logException(e);
2120 if (enabled.equals("true")) {
2121 if ("delete".equals(action)) {
2122 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2124 if ((!isDeleteResourceVersionOk)
2125 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2126 if ("create".equals(action) && !resourceVersion.equals("")) {
2127 errorDetail = "resource-version passed for " + action + " of " + uri;
2128 aaiExceptionCode = "AAI_6135";
2129 } else if (resourceVersion.equals("")) {
2130 errorDetail = "resource-version not passed for " + action + " of " + uri;
2131 aaiExceptionCode = "AAI_6130";
2133 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2134 aaiExceptionCode = "AAI_6131";
2137 throw new AAIException(aaiExceptionCode, errorDetail);
2145 * Verify resource version for delete.
2147 * @param currentResourceVersion the current resource version
2148 * @param resourceVersion the resource version
2149 * @return true, if successful or false if there is a mismatch
2151 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2153 boolean isDeleteResourceVersionOk = true;
2154 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2155 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2157 if ((!currentResourceVersion.equals(resourceVersion))
2158 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2159 isDeleteResourceVersionOk = false;
2161 return isDeleteResourceVersionOk;
2165 * Convert from camel case.
2167 * @param name the name
2168 * @return the string
2170 private String convertFromCamelCase(String name) {
2172 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2174 NamingExceptions exceptions = NamingExceptions.getInstance();
2175 result = exceptions.getDBName(result);
2180 private boolean canModify(Introspector obj, String propName, String requestContext) {
2181 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2182 if (readOnly != null) {
2183 final String[] items = readOnly.split(",");
2184 for (String item : items) {
2185 if (requestContext.equals(item)) {
2193 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2195 SideEffectRunner runner = new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class)
2196 .addSideEffect(PrivateEdge.class).build();
2198 runner.execute(obj, self);
2201 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2203 SideEffectRunner runner =
2204 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2206 runner.execute(obj, self);
2209 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2211 SideEffectRunner runner =
2212 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
2214 runner.execute(obj, self);
2217 public double getDBTimeMsecs() {
2218 return (dbTimeMsecs);
2222 * Db to object With Filters
2223 * This is for a one-time run with Tenant Isloation to only filter relationships
2225 * @param obj the obj
2226 * @param v the vertex from the graph
2227 * @param depth the depth
2228 * @param nodeOnly specify if to exclude relationships or not
2229 * @param filterCousinNodes
2230 * @return the introspector
2231 * @throws AAIException the AAI exception
2232 * @throws IllegalAccessException the illegal access exception
2233 * @throws IllegalArgumentException the illegal argument exception
2234 * @throws InvocationTargetException the invocation target exception
2235 * @throws SecurityException the security exception
2236 * @throws InstantiationException the instantiation exception
2237 * @throws NoSuchMethodException the no such method exception
2238 * @throws UnsupportedEncodingException the unsupported encoding exception
2239 * @throws MalformedURLException the malformed URL exception
2240 * @throws AAIUnknownObjectException
2241 * @throws URISyntaxException
2243 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2244 List<String> filterCousinNodes, List<String> filterParentNodes)
2245 throws AAIException, UnsupportedEncodingException {
2246 return dbToObjectWithFilters(obj, v, seen, depth, nodeOnly,
2247 filterCousinNodes, filterParentNodes, false);
2251 * Db to object With Filters
2252 * This is for a one-time run with Tenant Isloation to only filter relationships
2253 * TODO: Chnage the original dbToObject to take filter parent/cousins
2255 * @param obj the obj
2256 * @param v the vertex from the graph
2257 * @param depth the depth
2258 * @param nodeOnly specify if to exclude relationships or not
2259 * @param filterCousinNodes
2260 * @param isSkipRelatedTo determine to incorporated related-to-property data
2261 * @return the introspector
2262 * @throws AAIException the AAI exception
2263 * @throws IllegalAccessException the illegal access exception
2264 * @throws IllegalArgumentException the illegal argument exception
2265 * @throws InvocationTargetException the invocation target exception
2266 * @throws SecurityException the security exception
2267 * @throws InstantiationException the instantiation exception
2268 * @throws NoSuchMethodException the no such method exception
2269 * @throws UnsupportedEncodingException the unsupported encoding exception
2270 * @throws MalformedURLException the malformed URL exception
2271 * @throws AAIUnknownObjectException
2272 * @throws URISyntaxException
2274 // TODO - See if you can merge the 2 dbToObjectWithFilters
2275 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2276 List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2277 throws AAIException, UnsupportedEncodingException {
2278 String cleanUp = FALSE;
2284 boolean modified = false;
2285 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2286 List<Object> getList = null;
2288 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2289 this.copySimpleProperty(property, obj, v);
2292 if (obj.isComplexType(property)) {
2293 /* container case */
2295 if (!property.equals("relationship-list") && depth >= 0) {
2296 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2297 Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly,
2298 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2299 if (result != null) {
2300 obj.setValue(property, argumentObject.getUnderlyingObject());
2303 } else if (property.equals("relationship-list") && !nodeOnly) {
2304 /* relationships need to be handled correctly */
2305 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2307 createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes, isSkipRelatedTo);
2308 if (relationshipList != null) {
2309 obj.setValue(property, relationshipList.getUnderlyingObject());
2314 } else if (obj.isListType(property)) {
2316 if (property.equals("any")) {
2319 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2320 if (obj.isComplexGenericType(property) && depth >= 0) {
2321 final String childDbName = convertFromCamelCase(genericType);
2322 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2325 boolean isThisParentRequired =
2326 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2328 EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
2331 rule = edgeRules.getRule(q);
2332 } catch (EdgeRuleNotFoundException e) {
2333 throw new NoEdgeRuleFoundException(e);
2334 } catch (AmbiguousRuleChoiceException e) {
2335 throw new MultipleEdgeRuleFoundException(e);
2337 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2338 Direction ruleDirection = rule.getDirection();
2339 List<Vertex> verticesList = new ArrayList<>();
2340 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2341 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2342 verticesList.add(vertex);
2345 if (!verticesList.isEmpty()) {
2346 getList = obj.getValue(property);
2349 for (Vertex childVertex : verticesList) {
2350 if (!seen.contains(childVertex)) {
2351 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2353 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2354 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2355 if (result != null) {
2356 getList.add(argumentObject.getUnderlyingObject());
2361 LOGGER.warn("Cycle found while serializing vertex id={}",
2362 childVertex.id().toString());
2365 if (processed == 0) {
2366 // vertices were all seen, reset the list
2369 if (processed > 0) {
2373 } else if (obj.isSimpleGenericType(property)) {
2374 List<Object> temp = this.engine.getListProperty(v, property);
2376 getList = obj.getValue(property);
2377 getList.addAll(temp);
2388 // no changes were made to this obj, discard the instance
2392 this.enrichData(obj, v);
2398 * Creates the relationship list with the filtered node types.
2401 * @param obj the obj
2402 * @param cleanUp the clean up
2403 * @return the object
2404 * @throws IllegalArgumentException the illegal argument exception
2405 * @throws SecurityException the security exception
2406 * @throws UnsupportedEncodingException the unsupported encoding exception
2407 * @throws AAIException the AAI exception
2409 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp,
2410 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2411 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2413 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2414 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2415 return filterNodes.parallelStream().anyMatch(node::contains);
2418 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2420 List<Vertex> cousins = new ArrayList<>();
2421 cousinVertices.forEachRemaining(cousins::add);
2422 for (Vertex cousin : cousins) {
2424 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2425 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2426 if (result != null) {
2427 relationshipObjList.add(result);
2432 if (relationshipObjList.isEmpty()) {
2439 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2440 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2441 return this.edgeVertexes;
2444 public void addVertexToEdgeVertexes(Vertex vertex){
2445 this.edgeVertexes.add(vertex);
2448 private String urlToUri(String url) {
2449 if (url.startsWith("/")) {
2450 url = url.substring(1);
2453 if (url.endsWith("/")) {
2454 url = url.substring(0, url.length() - 1);
2457 // TODO - Check if this makes to do for model driven for base uri path
2458 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2459 if (url.charAt(0) != '/') {