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 Set<String> groups;
100 private final ModelType introspectionType;
101 private final SchemaVersion version;
102 private final Loader latestLoader;
103 private EdgeSerializer edgeSer;
104 private EdgeIngestor edgeRules;
105 private final Loader loader;
106 private final String baseURL;
107 private double dbTimeMsecs = 0;
108 private long currentTimeMillis;
110 private SchemaVersions schemaVersions;
111 private Set<String> namedPropNodes;
112 private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
113 private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
114 private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
115 private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>();
116 private int notificationDepth;
117 private boolean isDeltaEventsEnabled;
118 private boolean isMultiTenancyEnabled;
121 * Instantiates a new DB serializer.
123 * @param version the version
124 * @param engine the engine
125 * @param introspectionType the introspection type
126 * @param sourceOfTruth the source of truth
127 * @throws AAIException
129 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
130 String sourceOfTruth) throws AAIException {
131 this.engine = engine;
132 this.sourceOfTruth = sourceOfTruth;
133 this.groups = Collections.EMPTY_SET;
134 this.introspectionType = introspectionType;
135 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
136 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
138 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
139 this.version = version;
141 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
142 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
143 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
144 this.currentTimeMillis = System.currentTimeMillis();
145 // If creating the DBSerializer the old way then set the notification depth to maximum
146 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
150 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
151 String sourceOfTruth, Set<String> groups) throws AAIException {
152 this.engine = engine;
153 this.sourceOfTruth = sourceOfTruth;
154 this.groups = groups;
155 this.introspectionType = introspectionType;
156 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
157 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
159 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
160 this.version = version;
162 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
163 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
164 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
165 this.currentTimeMillis = System.currentTimeMillis();
166 // If creating the DBSerializer the old way then set the notification depth to maximum
167 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
171 public DBSerializer(SchemaVersion version,
172 TransactionalGraphEngine engine,
173 ModelType introspectionType,
174 String sourceOfTruth,
175 int notificationDepth) throws AAIException {
176 this.engine = engine;
177 this.sourceOfTruth = sourceOfTruth;
178 this.groups = Collections.EMPTY_SET;
179 this.introspectionType = introspectionType;
180 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
181 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
183 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
184 this.version = version;
186 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
187 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
188 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
189 this.currentTimeMillis = System.currentTimeMillis();
190 this.notificationDepth = notificationDepth;
194 public DBSerializer(SchemaVersion version,
195 TransactionalGraphEngine engine,
196 ModelType introspectionType,
197 String sourceOfTruth,
199 int notificationDepth) throws AAIException {
200 this.engine = engine;
201 this.sourceOfTruth = sourceOfTruth;
202 this.groups = groups;
203 this.introspectionType = introspectionType;
204 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
205 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
207 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
208 this.version = version;
210 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
211 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
212 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
213 this.currentTimeMillis = System.currentTimeMillis();
214 this.notificationDepth = notificationDepth;
218 public DBSerializer(SchemaVersion version,
219 TransactionalGraphEngine engine,
220 ModelType introspectionType,
221 String sourceOfTruth,
222 int notificationDepth,
223 String serverBase) throws AAIException {
224 this.engine = engine;
225 this.sourceOfTruth = sourceOfTruth;
226 this.groups = Collections.EMPTY_SET;
227 this.introspectionType = introspectionType;
228 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
229 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
231 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
232 this.version = version;
234 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
235 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
236 this.baseURL = serverBase;
237 this.currentTimeMillis = System.currentTimeMillis();
238 this.notificationDepth = notificationDepth;
242 public DBSerializer(SchemaVersion version,
243 TransactionalGraphEngine engine,
244 ModelType introspectionType,
245 String sourceOfTruth,
247 int notificationDepth,
248 String serverBase) throws AAIException {
249 this.engine = engine;
250 this.sourceOfTruth = sourceOfTruth;
251 this.groups = groups;
252 this.introspectionType = introspectionType;
253 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
254 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
256 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
257 this.version = version;
259 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
260 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
261 this.baseURL = serverBase;
262 this.currentTimeMillis = System.currentTimeMillis();
263 this.notificationDepth = notificationDepth;
267 private void initBeans() {
268 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
269 ApplicationContext ctx = SpringContextAware.getApplicationContext();
270 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
272 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
273 setEdgeSerializer(es);
274 isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
275 isMultiTenancyEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("multi.tenancy.enabled", FALSE));
278 public void setEdgeSerializer(EdgeSerializer edgeSer) {
279 this.edgeSer = edgeSer;
282 public EdgeSerializer getEdgeSeriailizer() {
286 public void setEdgeIngestor(EdgeIngestor ei) {
290 public EdgeIngestor getEdgeIngestor() {
291 return this.edgeRules;
294 public Map<Vertex, Boolean> getUpdatedVertexes() {
295 return updatedVertexes;
298 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair(){
299 return impliedDeleteUriObjectPair;
302 public Set<String> getGroups() {
307 * Touch standard vertex properties.
309 * @param isNewVertex the is new vertex
311 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
312 String timeNowInSec = Long.toString(currentTimeMillis);
314 String uuid = UUID.randomUUID().toString();
315 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
316 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
317 v.property(AAIProperties.AAI_UUID, uuid);
318 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
319 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
320 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
322 if(isDeltaEventsEnabled) {
323 standardVertexPropsDeltas(v, timeNowInSec);
325 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
326 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
327 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
331 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
332 String uri = v.property(AAIProperties.AAI_URI).value().toString();
333 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
334 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
335 if (getObjectDeltas().containsKey(uri)) {
336 getObjectDeltas().get(uri).setAction(objDeltaAction);
339 addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()), objDeltaAction);
340 addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()), objDeltaAction);
341 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()), objDeltaAction);
342 addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()), objDeltaAction);
344 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
347 AAIProperties.RESOURCE_VERSION,
348 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
353 AAIProperties.LAST_MOD_TS,
354 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
359 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
360 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
364 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
365 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
366 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
370 public Map<String, ObjectDelta> getObjectDeltas() {return objectDeltas;}
372 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
373 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
374 objectDelta.addPropertyDelta(prop, delta);
375 objectDeltas.put(uri, objectDelta);
378 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
379 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
380 objectDelta.addRelationshipDelta(delta);
381 objectDeltas.put(uri, objectDelta);
384 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
385 v.property(AAIProperties.NODE_TYPE, nodeType);
386 touchStandardVertexProperties(v, isNewVertex);
390 * Creates the new vertex.
392 * @param wrappedObject the wrapped object
395 public Vertex createNewVertex(Introspector wrappedObject) {
398 StopWatch.conditionalStart();
399 v = this.engine.tx().addVertex(wrappedObject.getDbName());
400 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
402 dbTimeMsecs += StopWatch.stopIfStarted();
410 * @param className the class name
414 * Removes the classpath from a class name
416 public String trimClassName(String className) {
417 String returnValue = "";
419 if (className.lastIndexOf('.') == -1) {
422 returnValue = className.substring(className.lastIndexOf('.') + 1);
432 * @param uriQuery the uri query
433 * @param identifier the identifier
434 * @throws SecurityException the security exception
435 * @throws IllegalArgumentException the illegal argument exception
436 * @throws AAIException the AAI exception
437 * @throws UnsupportedEncodingException the unsupported encoding exception
439 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
440 String requestContext) throws AAIException, UnsupportedEncodingException {
441 StopWatch.conditionalStart();
443 if (uriQuery.isDependent()) {
444 // try to find the parent
445 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
446 if (!vertices.isEmpty()) {
447 Vertex parent = vertices.get(0);
448 this.reflectDependentVertex(parent, v, obj, requestContext);
450 dbTimeMsecs += StopWatch.stopIfStarted();
451 throw new AAIException("AAI_6114",
452 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
455 serializeSingleVertex(v, obj, requestContext);
458 } catch (SchemaViolationException e) {
459 dbTimeMsecs += StopWatch.stopIfStarted();
460 throw new AAIException("AAI_6117", e);
462 dbTimeMsecs += StopWatch.stopIfStarted();
465 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
466 throws UnsupportedEncodingException, AAIException {
467 StopWatch.conditionalStart();
469 boolean isTopLevel = obj.isTopLevel();
471 addUriIfNeeded(v, obj.getURI());
474 URI uri = this.getURIForVertex(v);
475 URIParser parser = new URIParser(this.loader, uri);
476 if (parser.validate()) {
477 addUriIfNeeded(v, uri.toString());
480 processObject(obj, v, requestContext);
481 } catch (SchemaViolationException e) {
482 throw new AAIException("AAI_6117", e);
484 dbTimeMsecs += StopWatch.stopIfStarted();
488 private void addUriIfNeeded(Vertex v, String uri) {
489 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
490 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
491 v.property(AAIProperties.AAI_URI, uri);
501 * @throws IllegalArgumentException the illegal argument exception
502 * @throws SecurityException the security exception
503 * @throws AAIException the AAI exception
504 * @throws UnsupportedEncodingException the unsupported encoding exception
507 * Helper method for reflectToDb
508 * Handles all the property setting
510 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
511 throws UnsupportedEncodingException, AAIException {
512 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
513 properties.remove(AAIProperties.RESOURCE_VERSION);
514 List<Vertex> dependentVertexes = new ArrayList<>();
515 List<Vertex> processedVertexes = new ArrayList<>();
517 boolean isComplexType ;
520 // If the notification depth is set to maximum
521 // this is the behavior of the expected clients
522 if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
523 if (!obj.isContainer()) {
524 this.touchStandardVertexProperties(v, false);
527 this.executePreSideEffects(obj, v);
528 for (String property : properties) {
529 final String propertyType;
530 propertyType = obj.getType(property);
531 isComplexType = obj.isComplexType(property);
532 isListType = obj.isListType(property);
533 Object value = obj.getValue(property);
535 if (!(isComplexType || isListType)) {
536 boolean canModify = this.canModify(obj, property, requestContext);
539 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
540 String dbProperty = property;
541 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
542 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
544 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
545 // data linked properties are ephemeral
546 // they are populated dynamically on GETs
549 Object oldValue = v.property(dbProperty).orElse(null);
550 String uri = getURIForVertex(v).toString();
552 if (!value.equals(oldValue)) {
553 if (propertyType.toLowerCase().contains(".long")) {
554 v.property(dbProperty, new Integer(((Long) value).toString()));
556 v.property(dbProperty, value);
558 if (isDeltaEventsEnabled) {
559 createDeltaProperty(uri, value, dbProperty, oldValue);
561 this.updatedVertexes.putIfAbsent(v, false);
564 if (oldValue != null) {
565 v.property(dbProperty).remove();
566 if (isDeltaEventsEnabled) {
567 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
569 this.updatedVertexes.putIfAbsent(v, false);
573 } else if (isListType) {
574 List<Object> list = (List<Object>) value;
575 if (obj.isComplexGenericType(property)) {
577 for (Object o : list) {
578 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
579 child.setURIChain(obj.getURI());
580 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
585 if (isDeltaEventsEnabled) {
586 String uri = getURIForVertex(v).toString();
587 List<Object> oldVal = engine.getListProperty(v, property);
588 engine.setListProperty(v, property, list);
589 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
590 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
591 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
593 } else { // is either a create or update and is handled by the called method
594 createDeltaProperty(uri, list, property, oldVal);
597 engine.setListProperty(v, property, list);
599 this.updatedVertexes.putIfAbsent(v, false);
602 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
603 // back to this method
604 if (value != null) { // effectively ignore complex properties not included in the object we're
606 if (value.getClass().isArray()) {
608 int length = Array.getLength(value);
609 for (int i = 0; i < length; i++) {
610 Object arrayElement = Array.get(value, i);
611 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
612 child.setURIChain(obj.getURI());
613 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
616 } else if (!property.equals("relationship-list")) {
618 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
619 if (introspector.isContainer()) {
620 dependentVertexes.addAll(
621 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
622 introspector.setURIChain(obj.getURI());
624 processedVertexes.addAll(processObject(introspector, v, requestContext));
627 dependentVertexes.addAll(
628 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
629 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
632 } else if (property.equals("relationship-list")) {
633 handleRelationships(obj, v);
638 this.writeThroughDefaults(v, obj);
639 /* handle those vertexes not touched */
640 for (Vertex toBeKept : processedVertexes) {
641 dependentVertexes.remove(toBeKept);
644 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
645 List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
647 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
648 for(Vertex curVertex : impliedDeleteVertices){
649 if(!curVertex.property("aai-uri").isPresent()){
650 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
653 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
654 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
656 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
658 if(!curObj.isTopLevel()){
659 curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
662 if(!impliedDeleteUriObjectPair.containsKey(curAaiUri)){
663 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
668 impliedDelete.delete(impliedDeleteVertices);
670 // touch svp using vertex list for what changed
671 // if the notification depth is zero
672 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
673 this.updatedVertexes.entrySet().stream()
674 .filter(e -> !e.getValue())
675 .filter(e -> !edgeVertexes.contains(e.getKey()))
677 this.touchStandardVertexProperties(e.getKey(), false);
681 this.executePostSideEffects(obj, v);
682 return processedVertexes;
685 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
686 if (oldValue == null) {
687 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
689 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
693 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
694 Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
696 HashMap<String, Introspector> relatedVertices = new HashMap<>();
697 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
699 if (!aaiUriProperty.isPresent()) {
700 if (LOGGER.isDebugEnabled()) {
701 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
705 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
707 return relatedVertices;
710 String aaiUri = aaiUriProperty.value().toString();
712 if (!obj.isTopLevel()) {
713 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
714 List<Vertex> vertexChain;
715 // If the uriList is null then there is something wrong with converting the uri
716 // into a list of aai-uris so falling back to the old mechanism for finding parents
717 if (uriList == null) {
719 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
720 vertexChain = queryEngine.findParents(v);
721 } else if (uriList.length == 1) {
722 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
723 vertexChain = Collections.singletonList(v);
725 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
726 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
727 // inject v into start of vertexChain
728 vertexChain.add(0, v);
730 for (Vertex vertex : vertexChain) {
732 final Introspector vertexObj = this.getVertexProperties(vertex);
733 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
734 } catch (AAIUnknownObjectException e) {
735 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
740 final Introspector vertexObj = this.getVertexProperties(v);
741 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
742 } catch (AAIUnknownObjectException e) {
743 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
747 return relatedVertices;
751 * Given an uri, introspector object and loader object
752 * it will check if the obj is top level object if it is,
753 * it will return immediately returning the uri passed in
754 * If it isn't, it will go through, get the uriTemplate
755 * from the introspector object and get the count of "/"s
756 * and remove that part of the uri using substring
757 * and keep doing that until the current object is top level
758 * Also added the max depth just so worst case scenario
759 * Then keep adding aai-uri to the list include the aai-uri passed in
760 * Convert that list into an array and return it
767 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
769 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
770 * it converts to /vservers/vserver
772 * lastIndexOf /vservers/vserver in
773 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
778 * Use substring to get the string from 0 to that lastIndexOf
779 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
781 * From this new aai-uri, generate a introspector from the URITOObject class
782 * and keep doing this until you
786 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
787 * @param obj - introspector object of the given starting vertex
788 * @param loader - Type of loader which will always be MoxyLoader to support model driven
789 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
790 * @throws UnsupportedEncodingException
791 * @throws AAIException
793 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
794 throws UnsupportedEncodingException, AAIException {
796 List<String> uriList = new ArrayList<>();
798 String truncatedUri = aaiUri;
799 int depth = AAIProperties.MAXIMUM_DEPTH;
800 uriList.add(truncatedUri);
802 while (depth >= 0 && !obj.isTopLevel()) {
803 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
805 if (template == null) {
806 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
810 int templateCount = StringUtils.countMatches(template, "/");
811 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
813 if (templateCount > truncatedUriCount) {
814 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
818 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
819 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
820 uriList.add(truncatedUri);
821 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
825 return uriList.toArray(new String[0]);
829 * Handle relationships.
832 * @param vertex the vertex
833 * @throws SecurityException the security exception
834 * @throws IllegalArgumentException the illegal argument exception
835 * @throws UnsupportedEncodingException the unsupported encoding exception
836 * @throws AAIException the AAI exception
839 * Handles the explicit relationships defined for an obj
841 private void handleRelationships(Introspector obj, Vertex vertex)
842 throws UnsupportedEncodingException, AAIException {
844 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
845 processRelationshipList(wrappedRl, vertex);
850 * Process relationship list.
852 * @param wrapped the wrapped
854 * @throws UnsupportedEncodingException the unsupported encoding exception
855 * @throws AAIException the AAI exception
857 private void processRelationshipList(Introspector wrapped, Vertex v)
858 throws UnsupportedEncodingException, AAIException {
860 List<Object> relationships = wrapped.getValue("relationship");
861 String mainUri = getURIForVertex(v).toString();
862 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
863 EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
864 .edgeType(EdgeType.COUSIN)
865 .version(wrapped.getVersion());
866 EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
867 .edgeType(EdgeType.TREE)
868 .version(wrapped.getVersion());
870 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
872 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
873 for (Object relationship : relationships) {
876 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
877 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
879 if (relUri.startsWith("/vnf/")) {
880 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
881 List<Vertex> results = parser.getQueryBuilder().toList();
882 if (results.isEmpty()) {
883 final AAIException ex = new AAIException(AAI_6129,
884 String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
885 ex.getTemplateVars().add(parser.getResultType());
886 ex.getTemplateVars().add(parser.getUri().toString());
889 // still an issue if there's more than one
890 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
891 relUri = results.get(0).value(AAIProperties.AAI_URI);
893 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
899 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
900 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
902 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
903 String bNodeType = uriToObject.getEntityName();
904 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
905 if (!edgeIngestor.hasRule(ruleQuery)) {
906 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
907 if (edgeIngestor.hasRule(treeQuery)) {
908 throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
910 throw new AAIException("AAI_6120", String.format(
911 "No EdgeRule found for passed nodeTypes: %s, %s.",
912 aNodeType, bNodeType));
915 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
916 if (rules.size() == 1) {
917 label = rules.get(0).getLabel();
919 Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
920 if (defaultRule.isPresent()) {
921 label = defaultRule.get().getLabel();
923 throw new AAIException(AAI_6145);
926 } catch (EdgeRuleNotFoundException ea) {
927 throw new AAIException(AAI_6145, ea);
931 cousinUriAndLabels.add(Pair.with(relUri, label));
934 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
935 Set<Path> toRemove = new HashSet<>();
938 // for each path 3 things can happen:
939 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
940 // 2. The edge rule exits in this version it's included in the request the edge is left alone
941 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
942 for (Path path : paths) {
943 if (path.size() < 3) {
948 // v ----related-to--> otherV
949 // In the above case,
950 // path objects get(0) returns vertex v
951 // path objects.get(1) returns edge related-to
952 // path objects.get(2) returns vertex otherV
953 Vertex otherV = path.get(2);
956 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
957 bUri = otherV.value(AAIProperties.AAI_URI);
961 String edgeLabel = path.<Edge>get(1).label();
963 Pair<String, String> key = Pair.with(bUri, edgeLabel);
964 if (cousinUriAndLabels.contains(key)) {
965 cousinUriAndLabels.remove(key);
968 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
969 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
973 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
974 if (edgeIngestor.hasRule(ruleQuery)) {
981 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
982 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
985 String label = cousinUriAndLabel.getValue1();
986 String cousinUri = cousinUriAndLabel.getValue0();
987 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
989 List<Vertex> results = parser.getQueryBuilder().toList();
990 if (results.isEmpty()) {
991 final AAIException ex = new AAIException(AAI_6129,
992 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
993 ex.getTemplateVars().add(parser.getResultType());
994 ex.getTemplateVars().add(parser.getUri().toString());
997 // still an issue if there's more than one
998 cousinVertex = results.get(0);
1001 if (cousinVertex != null) {
1002 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
1003 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
1004 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
1006 if (!edgeRules.hasRule(baseQ.build())) {
1007 throw new AAIException("AAI_6120", String.format(
1008 "No EdgeRule found for passed nodeTypes: %s, %s%s.",
1009 aNodeType, cousinType, label != null ? (" with label " + label) : ""));
1010 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
1011 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
1012 throw new AAIException(AAI_6145);
1015 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
1018 toBeCreated.add(Pair.with(cousinVertex, label));
1023 for (Path path : toRemove) {
1024 if(isDeltaEventsEnabled) {
1025 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1027 this.updatedVertexes.putIfAbsent(v, false);
1028 this.edgeVertexes.add(path.get(2));
1029 path.<Edge>get(1).remove();
1032 for (Pair<Vertex, String> create : toBeCreated) {
1034 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
1035 if (isDeltaEventsEnabled) {
1036 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1038 this.updatedVertexes.putIfAbsent(v, false);
1039 this.edgeVertexes.add(create.getValue0());
1040 } catch (NoEdgeRuleFoundException ex) {
1041 throw new AAIException(AAI_6129, ex);
1047 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
1048 RelationshipDelta relationshipDelta = new RelationshipDelta(
1050 edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
1051 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
1052 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
1053 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
1055 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
1056 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
1060 * Write through defaults.
1063 * @param obj the obj
1064 * @throws AAIUnknownObjectException
1066 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
1067 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
1068 if (latest != null) {
1069 Set<String> required = latest.getRequiredProperties();
1071 for (String field : required) {
1072 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
1073 if (defaultValue != null) {
1074 Object vertexProp = v.property(field).orElse(null);
1075 if (vertexProp == null) {
1076 v.property(field, defaultValue);
1085 * Reflect dependent vertex.
1088 * @param dependentObj the dependent obj
1089 * @return the vertex
1090 * @throws IllegalArgumentException the illegal argument exception
1091 * @throws SecurityException the security exception
1092 * @throws AAIException the AAI exception
1093 * @throws UnsupportedEncodingException the unsupported encoding exception
1094 * @throws AAIUnknownObjectException
1096 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1097 throws AAIException, UnsupportedEncodingException {
1099 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1100 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1101 query.createKeyQuery(dependentObj);
1103 List<Vertex> items = query.toList();
1105 Vertex dependentVertex;
1106 if (items.size() == 1) {
1107 dependentVertex = items.get(0);
1108 this.verifyResourceVersion("update", dependentObj.getDbName(),
1109 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1110 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1112 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1113 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1114 dependentVertex = createNewVertex(dependentObj);
1117 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1122 * Reflect dependent vertex.
1124 * @param parent the parent
1125 * @param child the child
1126 * @param obj the obj
1127 * @return the vertex
1128 * @throws IllegalArgumentException the illegal argument exception
1129 * @throws SecurityException the security exception
1130 * @throws AAIException the AAI exception
1131 * @throws UnsupportedEncodingException the unsupported encoding exception
1132 * @throws AAIUnknownObjectException
1134 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1135 throws AAIException, UnsupportedEncodingException {
1137 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1138 if (parentUri != null) {
1141 addUriIfNeeded(child, parentUri + uri);
1143 processObject(obj, child, requestContext);
1146 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1149 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1150 if (canBeLinked != null && canBeLinked.equals("true")) {
1151 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1152 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1153 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1154 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1156 child.property(AAIProperties.LINKED, true);
1159 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1160 if(isDeltaEventsEnabled) {
1161 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1168 private SchemaVersion getVerForContext(String requestContext) {
1169 Pattern pattern = Pattern.compile("v[0-9]+");
1170 Matcher m = pattern.matcher(requestContext);
1172 return this.version;
1174 return new SchemaVersion(requestContext);
1181 * @param vertices the vertices
1182 * @param obj the obj
1183 * @param depth the depth
1184 * @param cleanUp the clean up
1185 * @return the introspector
1186 * @throws AAIException the AAI exception
1187 * @throws IllegalArgumentException the illegal argument exception
1188 * @throws SecurityException the security exception
1189 * @throws UnsupportedEncodingException the unsupported encoding exception
1190 * @throws AAIUnknownObjectException
1191 * @throws URISyntaxException
1193 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1194 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1195 final int internalDepth;
1196 if (depth == Integer.MAX_VALUE) {
1197 internalDepth = depth--;
1199 internalDepth = depth;
1201 StopWatch.conditionalStart();
1202 if (vertices.size() > 1 && !obj.isContainer()) {
1203 dbTimeMsecs += StopWatch.stopIfStarted();
1204 throw new AAIException("AAI_6136",
1205 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1206 } else if (obj.isContainer()) {
1208 String listProperty = null;
1209 for (String property : obj.getProperties()) {
1210 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1211 listProperty = property;
1215 final String propertyName = listProperty;
1216 getList = obj.getValue(listProperty);
1219 * This is an experimental multithreading experiment
1222 ExecutorService pool = GetAllPool.getInstance().getPool();
1224 List<Future<Object>> futures = new ArrayList<>();
1226 for (Vertex v : vertices) {
1227 AaiCallable<Object> task = new AaiCallable<Object>() {
1229 public Object process() throws UnsupportedEncodingException, AAIException {
1230 Set<Vertex> seen = new HashSet<>();
1231 Introspector childObject;
1232 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1233 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1234 return childObject.getUnderlyingObject();
1237 futures.add(pool.submit(task));
1240 for (Future<Object> future : futures) {
1242 getList.add(future.get());
1243 } catch (ExecutionException | InterruptedException e) {
1244 dbTimeMsecs += StopWatch.stopIfStarted();
1245 throw new AAIException("AAI_4000", e);
1248 } else if (vertices.size() == 1) {
1249 Set<Vertex> seen = new HashSet<>();
1250 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1255 dbTimeMsecs += StopWatch.stopIfStarted();
1262 * @param vertices the vertices
1263 * @param obj the obj
1264 * @param depth the depth
1265 * @param cleanUp the clean up
1266 * @return the introspector
1267 * @throws AAIException the AAI exception
1268 * @throws IllegalArgumentException the illegal argument exception
1269 * @throws SecurityException the security exception
1270 * @throws UnsupportedEncodingException the unsupported encoding exception
1271 * @throws AAIUnknownObjectException
1272 * @throws URISyntaxException
1274 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1275 String cleanUp) throws UnsupportedEncodingException, AAIException {
1276 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1282 * @param obj the obj
1284 * @param seen the seen
1285 * @param depth the depth
1286 * @param cleanUp the clean up
1287 * @return the introspector
1288 * @throws IllegalArgumentException the illegal argument exception
1289 * @throws SecurityException the security exception
1290 * @throws UnsupportedEncodingException the unsupported encoding exception
1291 * @throws AAIException the AAI exception
1292 * @throws AAIUnknownObjectException
1293 * @throws URISyntaxException
1295 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1296 String cleanUp) throws AAIException, UnsupportedEncodingException {
1297 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1303 * @param obj the obj
1305 * @param seen the seen
1306 * @param depth the depth
1307 * @param cleanUp the clean up
1308 * @return the introspector
1309 * @throws IllegalArgumentException the illegal argument exception
1310 * @throws SecurityException the security exception
1311 * @throws UnsupportedEncodingException the unsupported encoding exception
1312 * @throws AAIException the AAI exception
1313 * @throws AAIUnknownObjectException
1314 * @throws URISyntaxException
1316 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1317 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1325 boolean modified = false;
1326 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1327 List<Object> getList = null;
1329 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1330 this.copySimpleProperty(property, obj, v);
1333 if (obj.isComplexType(property)) {
1334 /* container case */
1336 if (!property.equals("relationship-list") && depth >= 0) {
1337 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1338 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1339 if (result != null) {
1340 obj.setValue(property, argumentObject.getUnderlyingObject());
1343 } else if (property.equals("relationship-list") && !nodeOnly) {
1344 /* relationships need to be handled correctly */
1345 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1346 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1347 if (relationshipList != null) {
1348 obj.setValue(property, relationshipList.getUnderlyingObject());
1353 } else if (obj.isListType(property)) {
1355 if (property.equals("any")) {
1358 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1359 if (obj.isComplexGenericType(property) && depth >= 0) {
1360 final String childDbName = convertFromCamelCase(genericType);
1361 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1365 rule = edgeRules.getRule(
1366 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1367 } catch (EdgeRuleNotFoundException e) {
1368 throw new NoEdgeRuleFoundException(e);
1369 } catch (AmbiguousRuleChoiceException e) {
1370 throw new MultipleEdgeRuleFoundException(e);
1372 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1374 Direction ruleDirection = rule.getDirection();
1375 List<Vertex> verticesList = new ArrayList<>();
1376 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1377 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1378 verticesList.add(vertex);
1381 if (!verticesList.isEmpty()) {
1382 getList = obj.getValue(property);
1385 for (Vertex childVertex : verticesList) {
1386 if (!seen.contains(childVertex)) {
1387 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1390 dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1391 if (result != null) {
1392 getList.add(argumentObject.getUnderlyingObject());
1397 LOGGER.warn("Cycle found while serializing vertex id={}",
1398 childVertex.id().toString());
1401 if (processed == 0) {
1402 // vertices were all seen, reset the list
1405 if (processed > 0) {
1409 } else if (obj.isSimpleGenericType(property)) {
1410 List<Object> temp = this.engine.getListProperty(v, property);
1412 getList = (List<Object>) obj.getValue(property);
1413 getList.addAll(temp);
1421 // no changes were made to this obj, discard the instance
1425 this.enrichData(obj, v);
1430 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1431 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1432 if (nodeType == null) {
1433 throw new AAIException("AAI_6143");
1436 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1437 Set<Vertex> seen = new HashSet<>();
1439 StopWatch.conditionalStart();
1440 this.dbToObject(obj, v, seen, depth, true, FALSE);
1441 dbTimeMsecs += StopWatch.stopIfStarted();
1446 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1447 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1450 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1451 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1452 if (nodeType == null) {
1453 throw new AAIException("AAI_6143");
1455 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1456 Set<Vertex> seen = new HashSet<>();
1457 StopWatch.conditionalStart();
1458 this.dbToObject(obj, v, seen, depth, false, FALSE);
1459 dbTimeMsecs += StopWatch.stopIfStarted();
1464 * Copy simple property.
1466 * @param property the property
1467 * @param obj the obj
1469 * @throws IllegalArgumentException the illegal argument exception
1470 * @throws SecurityException the security exception
1472 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1473 final Object temp = getProperty(obj, property, v);
1475 obj.setValue(property, temp);
1479 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1481 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1482 String[] simplePropsArray = new String[simpleProperties.size()];
1483 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1485 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1487 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1489 return simplePropsHashMap;
1492 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1493 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1494 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1495 return relationshipList;
1499 * Creates the relationship list.
1502 * @param obj the obj
1503 * @param cleanUp the clean up
1504 * @return the object
1505 * @throws IllegalArgumentException the illegal argument exception
1506 * @throws SecurityException the security exception
1507 * @throws UnsupportedEncodingException the unsupported encoding exception
1508 * @throws AAIException the AAI exception
1509 * @throws URISyntaxException
1511 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1512 throws UnsupportedEncodingException, AAIException {
1513 // default boolean value for isSkipRelatedTo is false
1514 return createRelationshipList(v, obj, cleanUp, false);
1518 * Creates the relationship list.
1521 * @param obj the obj
1522 * @param cleanUp the clean up
1523 * @param isSkipRelatedTo to determine adding related-to-property in response
1524 * @return the object
1525 * @throws IllegalArgumentException the illegal argument exception
1526 * @throws SecurityException the security exception
1527 * @throws UnsupportedEncodingException the unsupported encoding exception
1528 * @throws AAIException the AAI exception
1529 * @throws URISyntaxException
1531 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1532 throws UnsupportedEncodingException, AAIException {
1534 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1535 VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1537 if (!nodeTypeProperty.isPresent()) {
1538 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1542 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1544 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1546 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1548 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1549 .edgeType(EdgeType.COUSIN)
1550 .version(obj.getVersion());
1552 for (Path path : paths){
1553 if(path.size() < 3){
1558 // v ----related-to--> otherV
1559 // In the above case,
1560 // path objects get(0) returns vertex v
1561 // path objects.get(1) returns edge related-to
1562 // path objects.get(2) returns vertex otherV
1563 Edge edge = path.get(1);
1564 Vertex otherV= path.get(2);
1566 // TODO: Come back and revisit this code
1567 // Create a query based on the a nodetype and b nodetype
1568 // which is also a cousin edge and ensure the version
1569 // is used properly so for example in order to be backwards
1570 // compatible if we had allowed a edge between a and b
1571 // in a previous release and we decided to remove it from
1572 // the edge rules in the future we can display the edge
1573 // only for the older apis and the new apis if the edge rule
1574 // is removed will not be seen in the newer version of the API
1577 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1578 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1583 String edgeLabel = edge.label();
1584 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1586 if(!edgeIngestor.hasRule(ruleQuery)){
1587 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1591 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1592 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1593 if (result != null) {
1594 relationshipObjList.add(result);
1599 if (relationshipObjList.isEmpty()) {
1607 * Process edge relationship.
1609 * @param relationshipObj the relationship obj
1610 * @param edgeLabel the edge's label
1611 * @param cleanUp the clean up
1612 * @return the object
1613 * @throws IllegalArgumentException the illegal argument exception
1614 * @throws SecurityException the security exception
1615 * @throws UnsupportedEncodingException the unsupported encoding exception
1616 * @throws AAIUnknownObjectException
1617 * @throws URISyntaxException
1619 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1620 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1622 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1624 if (!aaiUriProperty.isPresent()) {
1628 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1630 URIToRelationshipObject uriParser;
1631 Introspector result;
1633 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1634 result = uriParser.getResult();
1635 } catch (AAIException | URISyntaxException e) {
1636 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1637 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1641 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1643 if (cousinVertexNodeType.isPresent()) {
1644 String cousinType = cousinVertexNodeType.value().toString();
1645 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1646 this.addRelatedToProperty(result, cousin, cousinType);
1650 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1651 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1654 return result.getUnderlyingObject();
1658 * Gets the URI for vertex.
1661 * @return the URI for vertex
1662 * @throws IllegalArgumentException the illegal argument exception
1663 * @throws SecurityException the security exception
1664 * @throws UnsupportedEncodingException the unsupported encoding exception
1665 * @throws AAIUnknownObjectException
1667 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1669 return getURIForVertex(v, false);
1672 public URI getURIForVertex(Vertex v, boolean overwrite) {
1673 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1675 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1677 if (aaiUri != null && !overwrite) {
1678 uri = UriBuilder.fromPath(aaiUri).build();
1685 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1686 throws AAIUnknownObjectException {
1691 obj = this.loader.introspectorFromName(cousinType);
1692 } catch (AAIUnknownObjectException ex) {
1693 if (LOGGER.isTraceEnabled()) {
1694 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1695 cousinType, cousinVertex.id());
1700 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1701 List<Introspector> relatedToProperties = new ArrayList<>();
1703 if (nameProps != null) {
1704 String[] props = nameProps.split(",");
1705 for (String prop : props) {
1706 final Object temp = getProperty(obj, prop, cousinVertex);
1707 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1708 relatedTo.setValue("property-key", cousinType + "." + prop);
1709 relatedTo.setValue("property-value", temp);
1710 relatedToProperties.add(relatedTo);
1714 if (!relatedToProperties.isEmpty()) {
1715 List<Object> relatedToList = relationship.getValue("related-to-property");
1716 for (Introspector introspector : relatedToProperties) {
1717 relatedToList.add(introspector.getUnderlyingObject());
1723 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1725 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1726 String dbPropertyName = prop;
1728 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1729 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1732 return vertex.<Object>property(dbPropertyName).orElse(null);
1738 * @param relationship the relationship
1739 * @param inputVertex the input vertex
1740 * @return true, if successful
1741 * @throws UnsupportedEncodingException the unsupported encoding exception
1742 * @throws AAIException the AAI exception
1744 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1745 throws UnsupportedEncodingException, AAIException {
1747 Vertex relatedVertex;
1748 StopWatch.conditionalStart();
1749 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1751 String label = null;
1752 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1753 label = relationship.getValue(RELATIONSHIP_LABEL);
1756 List<Vertex> results = parser.getQueryBuilder().toList();
1757 if (results.isEmpty()) {
1758 dbTimeMsecs += StopWatch.stopIfStarted();
1759 AAIException e = new AAIException(AAI_6129,
1760 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1761 e.getTemplateVars().add(parser.getResultType());
1762 e.getTemplateVars().add(parser.getUri().toString());
1765 // still an issue if there's more than one
1766 relatedVertex = results.get(0);
1769 if (relatedVertex != null) {
1773 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1775 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1776 if(isDeltaEventsEnabled) {
1777 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1780 // attempted to link two vertexes already linked
1783 dbTimeMsecs += StopWatch.stopIfStarted();
1787 dbTimeMsecs += StopWatch.stopIfStarted();
1788 return relatedVertex;
1792 * Gets all the edges between of the type with the specified label.
1794 * @param aVertex the out vertex
1795 * @param bVertex the in vertex
1796 * @return the edges between
1798 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1802 if (bVertex != null) {
1803 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1804 if (EdgeType.TREE.equals(type)) {
1805 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1806 if (edgeRule.getDirection().equals(Direction.IN)) {
1807 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1808 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1809 .not(__.has(EdgeField.PRIVATE.toString(), true));
1811 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1812 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1813 .not(__.has(EdgeField.PRIVATE.toString(), true));
1815 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1817 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1818 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1819 .not(__.has(EdgeField.PRIVATE.toString(), true));
1820 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1822 List<Edge> list = findEdgesBetween.toList();
1823 if (!list.isEmpty()) {
1824 result = list.get(0);
1832 * Gets all the edges string between of the type.
1834 * @param aVertex the out vertex
1835 * @param bVertex the in vertex
1836 * @return the edges between
1837 * @throws NoEdgeRuleFoundException
1839 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1841 List<String> result = new ArrayList<>();
1843 if (bVertex != null) {
1844 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1845 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1846 if (EdgeType.TREE.equals(type)) {
1847 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1848 __.has(EdgeField.PRIVATE.toString(), true)));
1850 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1851 .not(__.has(EdgeField.PRIVATE.toString(), true));
1853 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1854 result = findEdgesBetween.label().toList();
1860 * Gets all the edges between the vertexes with the label and type.
1862 * @param aVertex the out vertex
1863 * @param bVertex the in vertex
1865 * @return the edges between
1866 * @throws AAIException the AAI exception
1868 private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1872 if (bVertex != null) {
1873 String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1874 String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1875 EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1878 rule = edgeRules.getRule(q);
1879 } catch (EdgeRuleNotFoundException e) {
1880 throw new NoEdgeRuleFoundException(e);
1881 } catch (AmbiguousRuleChoiceException e) {
1882 throw new MultipleEdgeRuleFoundException(e);
1884 edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule);
1891 * Gets the edge between with the label and edge type.
1893 * @param aVertex the out vertex
1894 * @param bVertex the in vertex
1896 * @return the edge between
1897 * @throws AAIException the AAI exception
1898 * @throws NoEdgeRuleFoundException
1900 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1902 StopWatch.conditionalStart();
1903 if (bVertex != null) {
1905 Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label);
1907 dbTimeMsecs += StopWatch.stopIfStarted();
1912 dbTimeMsecs += StopWatch.stopIfStarted();
1916 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1917 return this.getEdgeBetween(type, aVertex, bVertex, null);
1923 * @param relationship the relationship
1924 * @param inputVertex the input vertex
1925 * @return true, if successful
1926 * @throws UnsupportedEncodingException the unsupported encoding exception
1927 * @throws AAIException the AAI exception
1929 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1930 throws UnsupportedEncodingException, AAIException {
1932 Vertex relatedVertex;
1933 StopWatch.conditionalStart();
1934 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1936 List<Vertex> results = parser.getQueryBuilder().toList();
1938 String label = null;
1939 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1940 label = relationship.getValue(RELATIONSHIP_LABEL);
1943 if (results.isEmpty()) {
1944 dbTimeMsecs += StopWatch.stopIfStarted();
1945 return Optional.empty();
1948 relatedVertex = results.get(0);
1951 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1952 } catch (NoEdgeRuleFoundException e) {
1953 dbTimeMsecs += StopWatch.stopIfStarted();
1954 throw new AAIException(AAI_6129, e);
1957 if(isDeltaEventsEnabled) {
1958 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1959 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1962 dbTimeMsecs += StopWatch.stopIfStarted();
1963 return Optional.of(relatedVertex);
1965 dbTimeMsecs += StopWatch.stopIfStarted();
1966 return Optional.empty();
1972 * Delete with traversal.
1974 * @param startVertex the start vertex
1976 public void deleteWithTraversal(Vertex startVertex) {
1977 StopWatch.conditionalStart();
1978 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1979 this.delete(results);
1983 * Removes the list of vertexes from the graph
1985 * Current the vertex label will just be vertex but
1986 * in the future the aai-node-type property will be replaced
1987 * by using the vertex label as when retrieving an vertex
1988 * and retrieving an single property on an vertex will pre-fetch
1989 * all the properties of that vertex and this is due to the following property
1992 * query.fast-property=true
1995 * JanusGraph doesn't provide the capability to override that
1996 * at a transaction level and there is a plan to move to vertex label
1997 * so it is best to utilize this for now and when the change is applied
1999 * @param vertices - list of vertices to delete from the graph
2001 void delete(List<Vertex> vertices) {
2002 StopWatch.conditionalStart();
2004 for (Vertex v : vertices) {
2005 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
2006 if(isDeltaEventsEnabled) {
2007 deltaForVertexDelete(v);
2009 //add the cousin vertexes of v to have their resource-version updated and notified on.
2010 v.edges(Direction.BOTH)
2011 .forEachRemaining(e -> {
2012 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
2013 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
2014 e.bothVertices().forEachRemaining(cousinV -> {
2015 if (!v.equals(cousinV)) {
2016 edgeVertexes.add(cousinV);
2022 //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
2023 // it should be removed from them as v is to be deleted
2024 edgeVertexes.remove(v);
2025 updatedVertexes.remove(v);
2029 dbTimeMsecs += StopWatch.stopIfStarted();
2032 private void deltaForVertexDelete(Vertex v) {
2033 String aaiUri = v.property(AAIProperties.AAI_URI).value().toString();
2034 v.keys().forEach(k -> {
2035 List<Object> list = new ArrayList<>();
2036 v.properties(k).forEachRemaining(vp -> list.add(vp.value()));
2037 if (list.size() == 1) {
2038 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)), DeltaAction.DELETE);
2040 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE);
2044 v.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
2051 * @param resourceVersion the resource version
2052 * @throws IllegalArgumentException the illegal argument exception
2053 * @throws AAIException the AAI exception
2054 * @throws InterruptedException the interrupted exception
2056 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
2057 throws IllegalArgumentException, AAIException {
2059 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2061 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
2062 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
2063 * So its better to make these in 2 steps
2065 if (result && !deletableVertices.isEmpty()) {
2066 result = verifyPreventDeleteSemantics(deletableVertices);
2071 deleteWithTraversal(v);
2072 } catch (IllegalStateException e) {
2073 throw new AAIException("AAI_6110", e);
2083 * @param resourceVersion the resource version
2084 * @throws IllegalArgumentException the illegal argument exception
2085 * @throws AAIException the AAI exception
2086 * @throws InterruptedException the interrupted exception
2088 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion)
2089 throws IllegalArgumentException, AAIException {
2091 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2096 deleteWithTraversal(v);
2097 } catch (IllegalStateException e) {
2098 throw new AAIException("AAI_6110", e);
2105 * Verify delete semantics.
2107 * @param vertex the vertex
2108 * @param resourceVersion the resource version
2109 * @return true, if successful
2110 * @throws AAIException the AAI exception
2112 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2113 throws AAIException {
2116 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2117 if (enableResourceVersion) {
2118 this.verifyResourceVersion("delete", nodeType,
2119 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2121 List<Vertex> vertices = new ArrayList<>();
2122 vertices.add(vertex);
2123 result = verifyPreventDeleteSemantics(vertices);
2129 * Verify Prevent delete semantics.
2131 * @param vertices the list of vertices
2132 * @return true, if successful
2133 * @throws AAIException the AAI exception
2135 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2136 boolean result = true;
2137 String errorDetail = " unknown delete semantic found";
2138 String aaiExceptionCode = "";
2140 StopWatch.conditionalStart();
2142 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2143 * "prevent-delete" condition
2144 * If yes - that should prevent the deletion of the vertex
2145 * Dedup makes sure we dont capture the prevent-delete vertices twice
2146 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2149 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2150 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2151 .values(AAIProperties.NODE_TYPE),
2152 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2153 .values(AAIProperties.NODE_TYPE))
2156 dbTimeMsecs += StopWatch.stopIfStarted();
2157 if (!preventDeleteVertices.isEmpty()) {
2158 aaiExceptionCode = "AAI_6110";
2159 errorDetail = String.format(
2160 "Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s",
2161 preventDeleteVertices);
2165 throw new AAIException(aaiExceptionCode, errorDetail);
2171 * Verify resource version.
2173 * @param action the action
2174 * @param nodeType the node type
2175 * @param currentResourceVersion the current resource version
2176 * @param resourceVersion the resource version
2177 * @param uri the uri
2178 * @return true, if successful
2179 * @throws AAIException the AAI exception
2181 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2182 String resourceVersion, String uri) throws AAIException {
2183 String enabled = "";
2184 String errorDetail = "";
2185 String aaiExceptionCode = "";
2186 boolean isDeleteResourceVersionOk = true;
2187 if (currentResourceVersion == null) {
2188 currentResourceVersion = "";
2191 if (resourceVersion == null) {
2192 resourceVersion = "";
2195 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2197 } catch (AAIException e) {
2198 ErrorLogHelper.logException(e);
2200 if (enabled.equals("true")) {
2201 if ("delete".equals(action)) {
2202 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2204 if ((!isDeleteResourceVersionOk)
2205 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2206 if ("create".equals(action) && !resourceVersion.equals("")) {
2207 errorDetail = "resource-version passed for " + action + " of " + uri;
2208 aaiExceptionCode = "AAI_6135";
2209 } else if (resourceVersion.equals("")) {
2210 errorDetail = "resource-version not passed for " + action + " of " + uri;
2211 aaiExceptionCode = "AAI_6130";
2213 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2214 aaiExceptionCode = "AAI_6131";
2217 throw new AAIException(aaiExceptionCode, errorDetail);
2225 * Verify resource version for delete.
2227 * @param currentResourceVersion the current resource version
2228 * @param resourceVersion the resource version
2229 * @return true, if successful or false if there is a mismatch
2231 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2233 boolean isDeleteResourceVersionOk = true;
2234 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2235 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2237 if ((!currentResourceVersion.equals(resourceVersion))
2238 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2239 isDeleteResourceVersionOk = false;
2241 return isDeleteResourceVersionOk;
2245 * Convert from camel case.
2247 * @param name the name
2248 * @return the string
2250 private String convertFromCamelCase(String name) {
2252 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2254 NamingExceptions exceptions = NamingExceptions.getInstance();
2255 result = exceptions.getDBName(result);
2260 private boolean canModify(Introspector obj, String propName, String requestContext) {
2261 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2262 if (readOnly != null) {
2263 final String[] items = readOnly.split(",");
2264 for (String item : items) {
2265 if (requestContext.equals(item)) {
2273 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2275 SideEffectRunner.Builder runnerBuilder =
2276 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class);
2277 if (isMultiTenancyEnabled) {
2278 runnerBuilder.addSideEffect(OwnerCheck.class);
2280 runnerBuilder.build().execute(obj, self);
2283 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2285 SideEffectRunner runner =
2286 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2288 runner.execute(obj, self);
2291 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2293 SideEffectRunner.Builder runnerBuilder =
2294 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class);
2296 if (isMultiTenancyEnabled) {
2297 runnerBuilder.addSideEffect(OwnerCheck.class);
2299 runnerBuilder.build().execute(obj, self);
2302 public double getDBTimeMsecs() {
2303 return (dbTimeMsecs);
2307 * Db to object With Filters
2308 * This is for a one-time run with Tenant Isloation to only filter relationships
2310 * @param obj the obj
2311 * @param v the vertex from the graph
2312 * @param depth the depth
2313 * @param nodeOnly specify if to exclude relationships or not
2314 * @param filterCousinNodes
2315 * @return the introspector
2316 * @throws AAIException the AAI exception
2317 * @throws IllegalAccessException the illegal access exception
2318 * @throws IllegalArgumentException the illegal argument exception
2319 * @throws InvocationTargetException the invocation target exception
2320 * @throws SecurityException the security exception
2321 * @throws InstantiationException the instantiation exception
2322 * @throws NoSuchMethodException the no such method exception
2323 * @throws UnsupportedEncodingException the unsupported encoding exception
2324 * @throws MalformedURLException the malformed URL exception
2325 * @throws AAIUnknownObjectException
2326 * @throws URISyntaxException
2328 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2329 List<String> filterCousinNodes, List<String> filterParentNodes)
2330 throws AAIException, UnsupportedEncodingException {
2331 return dbToObjectWithFilters(obj, v, seen, depth, nodeOnly,
2332 filterCousinNodes, filterParentNodes, false);
2336 * Db to object With Filters
2337 * This is for a one-time run with Tenant Isloation to only filter relationships
2338 * TODO: Chnage the original dbToObject to take filter parent/cousins
2340 * @param obj the obj
2341 * @param v the vertex from the graph
2342 * @param depth the depth
2343 * @param nodeOnly specify if to exclude relationships or not
2344 * @param filterCousinNodes
2345 * @param isSkipRelatedTo determine to incorporated related-to-property data
2346 * @return the introspector
2347 * @throws AAIException the AAI exception
2348 * @throws IllegalAccessException the illegal access exception
2349 * @throws IllegalArgumentException the illegal argument exception
2350 * @throws InvocationTargetException the invocation target exception
2351 * @throws SecurityException the security exception
2352 * @throws InstantiationException the instantiation exception
2353 * @throws NoSuchMethodException the no such method exception
2354 * @throws UnsupportedEncodingException the unsupported encoding exception
2355 * @throws MalformedURLException the malformed URL exception
2356 * @throws AAIUnknownObjectException
2357 * @throws URISyntaxException
2359 // TODO - See if you can merge the 2 dbToObjectWithFilters
2360 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
2361 List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2362 throws AAIException, UnsupportedEncodingException {
2363 String cleanUp = FALSE;
2369 boolean modified = false;
2370 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2371 List<Object> getList = null;
2373 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2374 this.copySimpleProperty(property, obj, v);
2377 if (obj.isComplexType(property)) {
2378 /* container case */
2380 if (!property.equals("relationship-list") && depth >= 0) {
2381 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2382 Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly,
2383 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2384 if (result != null) {
2385 obj.setValue(property, argumentObject.getUnderlyingObject());
2388 } else if (property.equals("relationship-list") && !nodeOnly) {
2389 /* relationships need to be handled correctly */
2390 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2392 createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes, isSkipRelatedTo);
2393 if (relationshipList != null) {
2394 obj.setValue(property, relationshipList.getUnderlyingObject());
2399 } else if (obj.isListType(property)) {
2401 if (property.equals("any")) {
2404 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2405 if (obj.isComplexGenericType(property) && depth >= 0) {
2406 final String childDbName = convertFromCamelCase(genericType);
2407 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2410 boolean isThisParentRequired =
2411 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2413 EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
2416 rule = edgeRules.getRule(q);
2417 } catch (EdgeRuleNotFoundException e) {
2418 throw new NoEdgeRuleFoundException(e);
2419 } catch (AmbiguousRuleChoiceException e) {
2420 throw new MultipleEdgeRuleFoundException(e);
2422 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2423 Direction ruleDirection = rule.getDirection();
2424 List<Vertex> verticesList = new ArrayList<>();
2425 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2426 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2427 verticesList.add(vertex);
2430 if (!verticesList.isEmpty()) {
2431 getList = obj.getValue(property);
2434 for (Vertex childVertex : verticesList) {
2435 if (!seen.contains(childVertex)) {
2436 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2438 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2439 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2440 if (result != null) {
2441 getList.add(argumentObject.getUnderlyingObject());
2446 LOGGER.warn("Cycle found while serializing vertex id={}",
2447 childVertex.id().toString());
2450 if (processed == 0) {
2451 // vertices were all seen, reset the list
2454 if (processed > 0) {
2458 } else if (obj.isSimpleGenericType(property)) {
2459 List<Object> temp = this.engine.getListProperty(v, property);
2461 getList = obj.getValue(property);
2462 getList.addAll(temp);
2473 // no changes were made to this obj, discard the instance
2477 this.enrichData(obj, v);
2483 * Creates the relationship list with the filtered node types.
2486 * @param obj the obj
2487 * @param cleanUp the clean up
2488 * @return the object
2489 * @throws IllegalArgumentException the illegal argument exception
2490 * @throws SecurityException the security exception
2491 * @throws UnsupportedEncodingException the unsupported encoding exception
2492 * @throws AAIException the AAI exception
2494 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp,
2495 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2496 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
2498 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2499 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2500 return filterNodes.parallelStream().anyMatch(node::contains);
2503 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2505 List<Vertex> cousins = new ArrayList<>();
2506 cousinVertices.forEachRemaining(cousins::add);
2507 for (Vertex cousin : cousins) {
2509 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2510 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2511 if (result != null) {
2512 relationshipObjList.add(result);
2517 if (relationshipObjList.isEmpty()) {
2524 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2525 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2526 return this.edgeVertexes;
2529 public void addVertexToEdgeVertexes(Vertex vertex){
2530 this.edgeVertexes.add(vertex);
2533 private String urlToUri(String url) {
2534 if (url.startsWith("/")) {
2535 url = url.substring(1);
2538 if (url.endsWith("/")) {
2539 url = url.substring(0, url.length() - 1);
2542 // TODO - Check if this makes to do for model driven for base uri path
2543 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2544 if (url.charAt(0) != '/') {