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 com.google.common.base.CaseFormat;
23 import java.io.UnsupportedEncodingException;
24 import java.lang.reflect.Array;
25 import java.lang.reflect.InvocationTargetException;
26 import java.net.MalformedURLException;
28 import java.net.URISyntaxException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.LinkedHashMap;
36 import java.util.LinkedHashSet;
37 import java.util.List;
39 import java.util.Optional;
41 import java.util.UUID;
42 import java.util.concurrent.ExecutionException;
43 import java.util.concurrent.ExecutorService;
44 import java.util.concurrent.Future;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47 import javax.ws.rs.core.UriBuilder;
48 import org.apache.commons.lang.StringUtils;
49 import org.apache.tinkerpop.gremlin.process.traversal.Path;
50 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
51 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
52 import org.apache.tinkerpop.gremlin.structure.Direction;
53 import org.apache.tinkerpop.gremlin.structure.Edge;
54 import org.apache.tinkerpop.gremlin.structure.Vertex;
55 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
56 import org.janusgraph.core.SchemaViolationException;
57 import org.javatuples.Pair;
58 import org.onap.aai.concurrent.AaiCallable;
59 import org.onap.aai.config.SpringContextAware;
60 import org.onap.aai.db.props.AAIProperties;
61 import org.onap.aai.edges.EdgeIngestor;
62 import org.onap.aai.edges.EdgeRule;
63 import org.onap.aai.edges.EdgeRuleQuery;
64 import org.onap.aai.edges.enums.AAIDirection;
65 import org.onap.aai.edges.enums.EdgeField;
66 import org.onap.aai.edges.enums.EdgeProperty;
67 import org.onap.aai.edges.enums.EdgeType;
68 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
69 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
70 import org.onap.aai.exceptions.AAIException;
71 import org.onap.aai.introspection.Introspector;
72 import org.onap.aai.introspection.IntrospectorFactory;
73 import org.onap.aai.introspection.Loader;
74 import org.onap.aai.introspection.LoaderFactory;
75 import org.onap.aai.introspection.ModelType;
76 import org.onap.aai.introspection.PropertyPredicates;
77 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
78 import org.onap.aai.introspection.sideeffect.DataCopy;
79 import org.onap.aai.introspection.sideeffect.DataLinkReader;
80 import org.onap.aai.introspection.sideeffect.DataLinkWriter;
81 import org.onap.aai.introspection.sideeffect.OwnerCheck;
82 import org.onap.aai.introspection.sideeffect.PrivateEdge;
83 import org.onap.aai.introspection.sideeffect.SideEffectRunner;
84 import org.onap.aai.logging.ErrorLogHelper;
85 import org.onap.aai.logging.LogFormatTools;
86 import org.onap.aai.logging.StopWatch;
87 import org.onap.aai.parsers.query.QueryParser;
88 import org.onap.aai.parsers.relationship.RelationshipToURI;
89 import org.onap.aai.parsers.uri.URIParser;
90 import org.onap.aai.parsers.uri.URIToObject;
91 import org.onap.aai.parsers.uri.URIToRelationshipObject;
92 import org.onap.aai.query.builder.QueryBuilder;
93 import org.onap.aai.schema.enums.ObjectMetadata;
94 import org.onap.aai.schema.enums.PropertyMetadata;
95 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
96 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
97 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
98 import org.onap.aai.serialization.engines.query.QueryEngine;
99 import org.onap.aai.setup.SchemaVersion;
100 import org.onap.aai.setup.SchemaVersions;
101 import org.onap.aai.util.AAIConfig;
102 import org.onap.aai.util.AAIConstants;
103 import org.onap.aai.util.delta.DeltaAction;
104 import org.onap.aai.util.delta.ObjectDelta;
105 import org.onap.aai.util.delta.PropertyDelta;
106 import org.onap.aai.util.delta.PropertyDeltaFactory;
107 import org.onap.aai.util.delta.RelationshipDelta;
108 import org.onap.aai.workarounds.NamingExceptions;
109 import org.slf4j.Logger;
110 import org.slf4j.LoggerFactory;
111 import org.springframework.context.ApplicationContext;
113 public class DBSerializer {
115 private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class);
116 private static final String RELATIONSHIP_LABEL = "relationship-label";
117 private static final String RELATIONSHIP = "relationship";
118 public static final String FALSE = "false";
119 public static final String AAI_6145 = "AAI_6145";
120 public static final String AAI_6129 = "AAI_6129";
122 private final TransactionalGraphEngine engine;
123 private final String sourceOfTruth;
124 private final Set<String> groups;
125 private final ModelType introspectionType;
126 private final SchemaVersion version;
127 private final Loader latestLoader;
128 private EdgeSerializer edgeSer;
129 private EdgeIngestor edgeRules;
130 private final Loader loader;
131 private final String baseURL;
132 private double dbTimeMsecs = 0;
133 private long currentTimeMillis;
135 private SchemaVersions schemaVersions;
136 private Set<String> namedPropNodes;
137 private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
138 private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
139 private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
140 private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>();
141 private int notificationDepth;
142 private boolean isDeltaEventsEnabled;
143 private boolean isMultiTenancyEnabled;
146 * Instantiates a new DB serializer.
148 * @param version the version
149 * @param engine the engine
150 * @param introspectionType the introspection type
151 * @param sourceOfTruth the source of truth
152 * @throws AAIException
154 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
155 String sourceOfTruth) throws AAIException {
156 this.engine = engine;
157 this.sourceOfTruth = sourceOfTruth;
158 this.groups = Collections.EMPTY_SET;
159 this.introspectionType = introspectionType;
160 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
161 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
163 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
164 this.version = version;
166 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
167 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
168 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
169 this.currentTimeMillis = System.currentTimeMillis();
170 // If creating the DBSerializer the old way then set the notification depth to maximum
171 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
175 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
176 String sourceOfTruth, Set<String> groups) throws AAIException {
177 this.engine = engine;
178 this.sourceOfTruth = sourceOfTruth;
179 this.groups = groups;
180 this.introspectionType = introspectionType;
181 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
182 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
184 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
185 this.version = version;
187 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
188 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
189 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
190 this.currentTimeMillis = System.currentTimeMillis();
191 // If creating the DBSerializer the old way then set the notification depth to maximum
192 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
196 public DBSerializer(SchemaVersion version,
197 TransactionalGraphEngine engine,
198 ModelType introspectionType,
199 String sourceOfTruth,
200 int notificationDepth) throws AAIException {
201 this.engine = engine;
202 this.sourceOfTruth = sourceOfTruth;
203 this.groups = Collections.EMPTY_SET;
204 this.introspectionType = introspectionType;
205 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
206 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
208 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
209 this.version = version;
211 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
212 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
213 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
214 this.currentTimeMillis = System.currentTimeMillis();
215 this.notificationDepth = notificationDepth;
219 public DBSerializer(SchemaVersion version,
220 TransactionalGraphEngine engine,
221 ModelType introspectionType,
222 String sourceOfTruth,
224 int notificationDepth) throws AAIException {
225 this.engine = engine;
226 this.sourceOfTruth = sourceOfTruth;
227 this.groups = groups;
228 this.introspectionType = introspectionType;
229 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
230 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
232 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
233 this.version = version;
235 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
236 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
237 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
238 this.currentTimeMillis = System.currentTimeMillis();
239 this.notificationDepth = notificationDepth;
243 public DBSerializer(SchemaVersion version,
244 TransactionalGraphEngine engine,
245 ModelType introspectionType,
246 String sourceOfTruth,
247 int notificationDepth,
248 String serverBase) throws AAIException {
249 this.engine = engine;
250 this.sourceOfTruth = sourceOfTruth;
251 this.groups = Collections.EMPTY_SET;
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 public DBSerializer(SchemaVersion version,
268 TransactionalGraphEngine engine,
269 ModelType introspectionType,
270 String sourceOfTruth,
272 int notificationDepth,
273 String serverBase) throws AAIException {
274 this.engine = engine;
275 this.sourceOfTruth = sourceOfTruth;
276 this.groups = groups;
277 this.introspectionType = introspectionType;
278 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
279 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
281 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
282 this.version = version;
284 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
285 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
286 this.baseURL = serverBase;
287 this.currentTimeMillis = System.currentTimeMillis();
288 this.notificationDepth = notificationDepth;
292 private void initBeans() {
293 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
294 ApplicationContext ctx = SpringContextAware.getApplicationContext();
295 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
297 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
298 setEdgeSerializer(es);
299 isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
300 isMultiTenancyEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("multi.tenancy.enabled", FALSE));
303 public void setEdgeSerializer(EdgeSerializer edgeSer) {
304 this.edgeSer = edgeSer;
307 public EdgeSerializer getEdgeSeriailizer() {
311 public void setEdgeIngestor(EdgeIngestor ei) {
315 public EdgeIngestor getEdgeIngestor() {
316 return this.edgeRules;
319 public Map<Vertex, Boolean> getUpdatedVertexes() {
320 return updatedVertexes;
323 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair() {
324 return impliedDeleteUriObjectPair;
327 public Set<String> getGroups() {
332 * Touch standard vertex properties.
334 * @param isNewVertex the is new vertex
336 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
337 String timeNowInSec = Long.toString(currentTimeMillis);
339 String uuid = UUID.randomUUID().toString();
340 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
341 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
342 v.property(AAIProperties.AAI_UUID, uuid);
343 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
344 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
345 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
347 if(isDeltaEventsEnabled) {
348 standardVertexPropsDeltas(v, timeNowInSec);
350 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
351 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
352 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
356 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
357 String uri = v.property(AAIProperties.AAI_URI).value().toString();
358 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
359 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
360 if (getObjectDeltas().containsKey(uri)) {
361 getObjectDeltas().get(uri).setAction(objDeltaAction);
364 addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory
365 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()),
367 addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory
368 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()),
370 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory
371 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()),
373 addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory
374 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()),
377 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
380 AAIProperties.RESOURCE_VERSION,
381 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
386 AAIProperties.LAST_MOD_TS,
387 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
392 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
393 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
397 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
398 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
399 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
403 public Map<String, ObjectDelta> getObjectDeltas() {
407 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
408 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
409 objectDelta.addPropertyDelta(prop, delta);
410 objectDeltas.put(uri, objectDelta);
413 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
414 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
415 objectDelta.addRelationshipDelta(delta);
416 objectDeltas.put(uri, objectDelta);
419 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
420 v.property(AAIProperties.NODE_TYPE, nodeType);
421 touchStandardVertexProperties(v, isNewVertex);
425 * Creates the new vertex.
427 * @param wrappedObject the wrapped object
430 public Vertex createNewVertex(Introspector wrappedObject) {
433 StopWatch.conditionalStart();
434 v = this.engine.tx().addVertex(wrappedObject.getDbName());
435 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
437 dbTimeMsecs += StopWatch.stopIfStarted();
445 * @param className the class name
449 * Removes the classpath from a class name
451 public String trimClassName(String className) {
452 String returnValue = "";
454 if (className.lastIndexOf('.') == -1) {
457 returnValue = className.substring(className.lastIndexOf('.') + 1);
467 * @param uriQuery the uri query
468 * @param identifier the identifier
469 * @throws SecurityException the security exception
470 * @throws IllegalArgumentException the illegal argument exception
471 * @throws AAIException the AAI exception
472 * @throws UnsupportedEncodingException the unsupported encoding exception
474 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
475 String requestContext) throws AAIException, UnsupportedEncodingException {
476 StopWatch.conditionalStart();
478 if (uriQuery.isDependent()) {
479 // try to find the parent
480 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
481 if (!vertices.isEmpty()) {
482 Vertex parent = vertices.get(0);
483 this.reflectDependentVertex(parent, v, obj, requestContext);
485 dbTimeMsecs += StopWatch.stopIfStarted();
486 throw new AAIException("AAI_6114",
487 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
490 serializeSingleVertex(v, obj, requestContext);
493 } catch (SchemaViolationException e) {
494 dbTimeMsecs += StopWatch.stopIfStarted();
495 throw new AAIException("AAI_6117", e);
497 dbTimeMsecs += StopWatch.stopIfStarted();
500 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
501 throws UnsupportedEncodingException, AAIException {
502 StopWatch.conditionalStart();
504 boolean isTopLevel = obj.isTopLevel();
506 addUriIfNeeded(v, obj.getURI());
509 URI uri = this.getURIForVertex(v);
510 URIParser parser = new URIParser(this.loader, uri);
511 if (parser.validate()) {
512 addUriIfNeeded(v, uri.toString());
515 processObject(obj, v, requestContext);
516 } catch (SchemaViolationException e) {
517 throw new AAIException("AAI_6117", e);
519 dbTimeMsecs += StopWatch.stopIfStarted();
523 private void addUriIfNeeded(Vertex v, String uri) {
524 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
525 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
526 v.property(AAIProperties.AAI_URI, uri);
536 * @throws IllegalArgumentException the illegal argument exception
537 * @throws SecurityException the security exception
538 * @throws AAIException the AAI exception
539 * @throws UnsupportedEncodingException the unsupported encoding exception
542 * Helper method for reflectToDb
543 * Handles all the property setting
545 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
546 throws UnsupportedEncodingException, AAIException {
547 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
548 properties.remove(AAIProperties.RESOURCE_VERSION);
549 List<Vertex> dependentVertexes = new ArrayList<>();
550 List<Vertex> processedVertexes = new ArrayList<>();
552 boolean isComplexType ;
555 // If the notification depth is set to maximum
556 // this is the behavior of the expected clients
557 if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
558 if (!obj.isContainer()) {
559 this.touchStandardVertexProperties(v, false);
562 this.executePreSideEffects(obj, v);
563 for (String property : properties) {
564 final String propertyType;
565 propertyType = obj.getType(property);
566 isComplexType = obj.isComplexType(property);
567 isListType = obj.isListType(property);
568 Object value = obj.getValue(property);
570 if (!(isComplexType || isListType)) {
571 boolean canModify = this.canModify(obj, property, requestContext);
574 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
575 String dbProperty = property;
576 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
577 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
579 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
580 // data linked properties are ephemeral
581 // they are populated dynamically on GETs
584 Object oldValue = v.property(dbProperty).orElse(null);
585 String uri = getURIForVertex(v).toString();
587 if (!value.equals(oldValue)) {
588 if (propertyType.toLowerCase().contains(".long")) {
589 v.property(dbProperty, new Integer(((Long) value).toString()));
591 v.property(dbProperty, value);
593 if (isDeltaEventsEnabled) {
594 createDeltaProperty(uri, value, dbProperty, oldValue);
596 this.updatedVertexes.putIfAbsent(v, false);
599 if (oldValue != null) {
600 v.property(dbProperty).remove();
601 if (isDeltaEventsEnabled) {
602 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
604 this.updatedVertexes.putIfAbsent(v, false);
608 } else if (isListType) {
609 List<Object> list = (List<Object>) value;
610 if (obj.isComplexGenericType(property)) {
612 for (Object o : list) {
613 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
614 child.setURIChain(obj.getURI());
615 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
620 if (isDeltaEventsEnabled) {
621 String uri = getURIForVertex(v).toString();
622 List<Object> oldVal = engine.getListProperty(v, property);
623 engine.setListProperty(v, property, list);
624 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
625 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
626 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
628 } else { // is either a create or update and is handled by the called method
629 createDeltaProperty(uri, list, property, oldVal);
632 engine.setListProperty(v, property, list);
634 this.updatedVertexes.putIfAbsent(v, false);
637 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
638 // back to this method
639 if (value != null) { // effectively ignore complex properties not included in the object we're
641 if (value.getClass().isArray()) {
643 int length = Array.getLength(value);
644 for (int i = 0; i < length; i++) {
645 Object arrayElement = Array.get(value, i);
646 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
647 child.setURIChain(obj.getURI());
648 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
651 } else if (!property.equals("relationship-list")) {
653 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
654 if (introspector.isContainer()) {
655 dependentVertexes.addAll(
656 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
657 introspector.setURIChain(obj.getURI());
659 processedVertexes.addAll(processObject(introspector, v, requestContext));
662 dependentVertexes.addAll(
663 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
664 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
667 } else if (property.equals("relationship-list")) {
668 handleRelationships(obj, v);
673 this.writeThroughDefaults(v, obj);
674 /* handle those vertexes not touched */
675 for (Vertex toBeKept : processedVertexes) {
676 dependentVertexes.remove(toBeKept);
679 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
680 List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
682 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
683 for (Vertex curVertex : impliedDeleteVertices) {
684 if (!curVertex.property("aai-uri").isPresent()) {
685 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
688 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
689 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
691 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
693 if (!curObj.isTopLevel()) {
694 curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
697 if (!impliedDeleteUriObjectPair.containsKey(curAaiUri)) {
698 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
703 impliedDelete.delete(impliedDeleteVertices);
705 // touch svp using vertex list for what changed
706 // if the notification depth is zero
707 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
708 this.updatedVertexes.entrySet().stream()
709 .filter(e -> !e.getValue())
710 .filter(e -> !edgeVertexes.contains(e.getKey()))
712 this.touchStandardVertexProperties(e.getKey(), false);
716 this.executePostSideEffects(obj, v);
717 return processedVertexes;
720 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
721 if (oldValue == null) {
722 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
724 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
728 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
729 Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
731 HashMap<String, Introspector> relatedVertices = new HashMap<>();
732 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
734 if (!aaiUriProperty.isPresent()) {
735 if (LOGGER.isDebugEnabled()) {
736 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
740 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
742 return relatedVertices;
745 String aaiUri = aaiUriProperty.value().toString();
747 if (!obj.isTopLevel()) {
748 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
749 List<Vertex> vertexChain;
750 // If the uriList is null then there is something wrong with converting the uri
751 // into a list of aai-uris so falling back to the old mechanism for finding parents
752 if (uriList == null) {
754 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
755 vertexChain = queryEngine.findParents(v);
756 } else if (uriList.length == 1) {
757 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
758 vertexChain = Collections.singletonList(v);
760 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
761 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
762 // inject v into start of vertexChain
763 vertexChain.add(0, v);
765 for (Vertex vertex : vertexChain) {
767 final Introspector vertexObj = this.getVertexProperties(vertex);
768 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
769 } catch (AAIUnknownObjectException e) {
770 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
775 final Introspector vertexObj = this.getVertexProperties(v);
776 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
777 } catch (AAIUnknownObjectException e) {
778 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
782 return relatedVertices;
786 * Given an uri, introspector object and loader object
787 * it will check if the obj is top level object if it is,
788 * it will return immediately returning the uri passed in
789 * If it isn't, it will go through, get the uriTemplate
790 * from the introspector object and get the count of "/"s
791 * and remove that part of the uri using substring
792 * and keep doing that until the current object is top level
793 * Also added the max depth just so worst case scenario
794 * Then keep adding aai-uri to the list include the aai-uri passed in
795 * Convert that list into an array and return it
802 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
804 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
805 * it converts to /vservers/vserver
807 * lastIndexOf /vservers/vserver in
808 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
813 * Use substring to get the string from 0 to that lastIndexOf
814 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
816 * From this new aai-uri, generate a introspector from the URITOObject class
817 * and keep doing this until you
821 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
822 * @param obj - introspector object of the given starting vertex
823 * @param loader - Type of loader which will always be MoxyLoader to support model driven
824 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
825 * @throws UnsupportedEncodingException
826 * @throws AAIException
828 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
829 throws UnsupportedEncodingException, AAIException {
831 List<String> uriList = new ArrayList<>();
833 String truncatedUri = aaiUri;
834 int depth = AAIProperties.MAXIMUM_DEPTH;
835 uriList.add(truncatedUri);
837 while (depth >= 0 && !obj.isTopLevel()) {
838 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
840 if (template == null) {
841 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
845 int templateCount = StringUtils.countMatches(template, "/");
846 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
848 if (templateCount > truncatedUriCount) {
849 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
853 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
854 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
855 uriList.add(truncatedUri);
856 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
860 return uriList.toArray(new String[0]);
864 * Handle relationships.
867 * @param vertex the vertex
868 * @throws SecurityException the security exception
869 * @throws IllegalArgumentException the illegal argument exception
870 * @throws UnsupportedEncodingException the unsupported encoding exception
871 * @throws AAIException the AAI exception
874 * Handles the explicit relationships defined for an obj
876 private void handleRelationships(Introspector obj, Vertex vertex)
877 throws UnsupportedEncodingException, AAIException {
879 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
880 processRelationshipList(wrappedRl, vertex);
885 * Process relationship list.
887 * @param wrapped the wrapped
889 * @throws UnsupportedEncodingException the unsupported encoding exception
890 * @throws AAIException the AAI exception
892 private void processRelationshipList(Introspector wrapped, Vertex v)
893 throws UnsupportedEncodingException, AAIException {
895 List<Object> relationships = wrapped.getValue("relationship");
896 String mainUri = getURIForVertex(v).toString();
897 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
898 EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
899 .edgeType(EdgeType.COUSIN)
900 .version(wrapped.getVersion());
901 EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
902 .edgeType(EdgeType.TREE)
903 .version(wrapped.getVersion());
905 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
907 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
908 for (Object relationship : relationships) {
911 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
912 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
914 if (relUri.startsWith("/vnf/")) {
915 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
916 List<Vertex> results = parser.getQueryBuilder().toList();
917 if (results.isEmpty()) {
918 final AAIException ex = new AAIException(AAI_6129,
919 String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
920 ex.getTemplateVars().add(parser.getResultType());
921 ex.getTemplateVars().add(parser.getUri().toString());
924 // still an issue if there's more than one
925 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
926 relUri = results.get(0).value(AAIProperties.AAI_URI);
928 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
934 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
935 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
937 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
938 String bNodeType = uriToObject.getEntityName();
939 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
940 if (!edgeIngestor.hasRule(ruleQuery)) {
941 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
942 if (edgeIngestor.hasRule(treeQuery)) {
943 throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
945 throw new AAIException("AAI_6120", String.format(
946 "No EdgeRule found for passed nodeTypes: %s, %s.",
947 aNodeType, bNodeType));
950 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
951 if (rules.size() == 1) {
952 label = rules.get(0).getLabel();
955 defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
956 if (defaultRule.isPresent()) {
957 label = defaultRule.get().getLabel();
959 throw new AAIException(AAI_6145);
962 } catch (EdgeRuleNotFoundException ea) {
963 throw new AAIException(AAI_6145, ea);
967 cousinUriAndLabels.add(Pair.with(relUri, label));
970 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
971 Set<Path> toRemove = new HashSet<>();
974 // for each path 3 things can happen:
975 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
976 // 2. The edge rule exits in this version it's included in the request the edge is left alone
977 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
978 for (Path path : paths) {
979 if (path.size() < 3) {
984 // v ----related-to--> otherV
985 // In the above case,
986 // path objects get(0) returns vertex v
987 // path objects.get(1) returns edge related-to
988 // path objects.get(2) returns vertex otherV
989 Vertex otherV = path.get(2);
992 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
993 bUri = otherV.value(AAIProperties.AAI_URI);
997 String edgeLabel = path.<Edge>get(1).label();
999 Pair<String, String> key = Pair.with(bUri, edgeLabel);
1000 if (cousinUriAndLabels.contains(key)) {
1001 cousinUriAndLabels.remove(key);
1004 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1005 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
1009 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
1010 if (edgeIngestor.hasRule(ruleQuery)) {
1017 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
1018 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
1020 Vertex cousinVertex;
1021 String label = cousinUriAndLabel.getValue1();
1022 String cousinUri = cousinUriAndLabel.getValue0();
1023 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
1025 List<Vertex> results = parser.getQueryBuilder().toList();
1026 if (results.isEmpty()) {
1027 final AAIException ex = new AAIException(AAI_6129,
1028 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1029 ex.getTemplateVars().add(parser.getResultType());
1030 ex.getTemplateVars().add(parser.getUri().toString());
1033 // still an issue if there's more than one
1034 cousinVertex = results.get(0);
1037 if (cousinVertex != null) {
1038 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
1039 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
1040 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
1042 if (!edgeRules.hasRule(baseQ.build())) {
1043 throw new AAIException("AAI_6120", String.format(
1044 "No EdgeRule found for passed nodeTypes: %s, %s%s.",
1045 aNodeType, cousinType, label != null ? (" with label " + label) : ""));
1046 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
1047 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
1048 throw new AAIException(AAI_6145);
1051 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
1054 toBeCreated.add(Pair.with(cousinVertex, label));
1059 for (Path path : toRemove) {
1060 if(isDeltaEventsEnabled) {
1061 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1063 this.updatedVertexes.putIfAbsent(v, false);
1064 this.edgeVertexes.add(path.get(2));
1065 path.<Edge>get(1).remove();
1068 for (Pair<Vertex, String> create : toBeCreated) {
1070 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
1071 if (isDeltaEventsEnabled) {
1072 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1074 this.updatedVertexes.putIfAbsent(v, false);
1075 this.edgeVertexes.add(create.getValue0());
1076 } catch (NoEdgeRuleFoundException ex) {
1077 throw new AAIException(AAI_6129, ex);
1083 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
1084 RelationshipDelta relationshipDelta = new RelationshipDelta(
1086 edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
1087 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
1088 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
1089 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
1091 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
1092 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
1096 * Write through defaults.
1099 * @param obj the obj
1100 * @throws AAIUnknownObjectException
1102 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
1103 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
1104 if (latest != null) {
1105 Set<String> required = latest.getRequiredProperties();
1107 for (String field : required) {
1108 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
1109 if (defaultValue != null) {
1110 Object vertexProp = v.property(field).orElse(null);
1111 if (vertexProp == null) {
1112 v.property(field, defaultValue);
1121 * Reflect dependent vertex.
1124 * @param dependentObj the dependent obj
1125 * @return the vertex
1126 * @throws IllegalArgumentException the illegal argument exception
1127 * @throws SecurityException the security exception
1128 * @throws AAIException the AAI exception
1129 * @throws UnsupportedEncodingException the unsupported encoding exception
1130 * @throws AAIUnknownObjectException
1132 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1133 throws AAIException, UnsupportedEncodingException {
1135 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1136 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1137 query.createKeyQuery(dependentObj);
1139 List<Vertex> items = query.toList();
1141 Vertex dependentVertex;
1142 if (items.size() == 1) {
1143 dependentVertex = items.get(0);
1144 this.verifyResourceVersion("update", dependentObj.getDbName(),
1145 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1146 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1148 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1149 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1150 dependentVertex = createNewVertex(dependentObj);
1153 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1158 * Reflect dependent vertex.
1160 * @param parent the parent
1161 * @param child the child
1162 * @param obj the obj
1163 * @return the vertex
1164 * @throws IllegalArgumentException the illegal argument exception
1165 * @throws SecurityException the security exception
1166 * @throws AAIException the AAI exception
1167 * @throws UnsupportedEncodingException the unsupported encoding exception
1168 * @throws AAIUnknownObjectException
1170 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1171 throws AAIException, UnsupportedEncodingException {
1173 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1174 if (parentUri != null) {
1177 addUriIfNeeded(child, parentUri + uri);
1179 processObject(obj, child, requestContext);
1182 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1185 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1186 if (canBeLinked != null && canBeLinked.equals("true")) {
1187 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1188 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1189 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1190 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1192 child.property(AAIProperties.LINKED, true);
1195 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1196 if(isDeltaEventsEnabled) {
1197 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1204 private SchemaVersion getVerForContext(String requestContext) {
1205 Pattern pattern = Pattern.compile("v[0-9]+");
1206 Matcher m = pattern.matcher(requestContext);
1208 return this.version;
1210 return new SchemaVersion(requestContext);
1217 * @param vertices the vertices
1218 * @param obj the obj
1219 * @param depth the depth
1220 * @param cleanUp the clean up
1221 * @return the introspector
1222 * @throws AAIException the AAI exception
1223 * @throws IllegalArgumentException the illegal argument exception
1224 * @throws SecurityException the security exception
1225 * @throws UnsupportedEncodingException the unsupported encoding exception
1226 * @throws AAIUnknownObjectException
1227 * @throws URISyntaxException
1229 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1230 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1231 final int internalDepth;
1232 if (depth == Integer.MAX_VALUE) {
1233 internalDepth = depth--;
1235 internalDepth = depth;
1237 StopWatch.conditionalStart();
1238 if (vertices.size() > 1 && !obj.isContainer()) {
1239 dbTimeMsecs += StopWatch.stopIfStarted();
1240 throw new AAIException("AAI_6136",
1241 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1242 } else if (obj.isContainer()) {
1244 String listProperty = null;
1245 for (String property : obj.getProperties()) {
1246 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1247 listProperty = property;
1251 final String propertyName = listProperty;
1252 getList = obj.getValue(listProperty);
1255 * This is an experimental multithreading experiment
1258 ExecutorService pool = GetAllPool.getInstance().getPool();
1260 List<Future<Object>> futures = new ArrayList<>();
1262 for (Vertex v : vertices) {
1263 AaiCallable<Object> task = new AaiCallable<Object>() {
1265 public Object process() throws UnsupportedEncodingException, AAIException {
1266 Set<Vertex> seen = new HashSet<>();
1267 Introspector childObject;
1268 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1269 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1270 return childObject.getUnderlyingObject();
1273 futures.add(pool.submit(task));
1276 for (Future<Object> future : futures) {
1278 getList.add(future.get());
1279 } catch (InterruptedException e) {
1280 dbTimeMsecs += StopWatch.stopIfStarted();
1281 Thread.currentThread().interrupt();
1282 throw new AAIException("AAI_4000", e);
1283 } catch (ExecutionException e) {
1284 dbTimeMsecs += StopWatch.stopIfStarted();
1285 throw new AAIException("AAI_4000", e);
1288 } else if (vertices.size() == 1) {
1289 Set<Vertex> seen = new HashSet<>();
1290 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1295 dbTimeMsecs += StopWatch.stopIfStarted();
1302 * @param vertices the vertices
1303 * @param obj the obj
1304 * @param depth the depth
1305 * @param cleanUp the clean up
1306 * @return the introspector
1307 * @throws AAIException the AAI exception
1308 * @throws IllegalArgumentException the illegal argument exception
1309 * @throws SecurityException the security exception
1310 * @throws UnsupportedEncodingException the unsupported encoding exception
1311 * @throws AAIUnknownObjectException
1312 * @throws URISyntaxException
1314 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1315 String cleanUp) throws UnsupportedEncodingException, AAIException {
1316 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1322 * @param obj the obj
1324 * @param seen the seen
1325 * @param depth the depth
1326 * @param cleanUp the clean up
1327 * @return the introspector
1328 * @throws IllegalArgumentException the illegal argument exception
1329 * @throws SecurityException the security exception
1330 * @throws UnsupportedEncodingException the unsupported encoding exception
1331 * @throws AAIException the AAI exception
1332 * @throws AAIUnknownObjectException
1333 * @throws URISyntaxException
1335 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1336 String cleanUp) throws AAIException, UnsupportedEncodingException {
1337 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1343 * @param obj the obj
1345 * @param seen the seen
1346 * @param depth the depth
1347 * @param cleanUp the clean up
1348 * @return the introspector
1349 * @throws IllegalArgumentException the illegal argument exception
1350 * @throws SecurityException the security exception
1351 * @throws UnsupportedEncodingException the unsupported encoding exception
1352 * @throws AAIException the AAI exception
1353 * @throws AAIUnknownObjectException
1354 * @throws URISyntaxException
1356 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1357 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1365 boolean modified = false;
1366 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1367 List<Object> getList = null;
1369 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1370 this.copySimpleProperty(property, obj, v);
1373 if (obj.isComplexType(property)) {
1374 /* container case */
1376 if (!property.equals("relationship-list") && depth >= 0) {
1377 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1378 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1379 if (result != null) {
1380 obj.setValue(property, argumentObject.getUnderlyingObject());
1383 } else if (property.equals("relationship-list") && !nodeOnly) {
1384 /* relationships need to be handled correctly */
1385 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1386 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1387 if (relationshipList != null) {
1388 obj.setValue(property, relationshipList.getUnderlyingObject());
1393 } else if (obj.isListType(property)) {
1395 if (property.equals("any")) {
1398 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1399 if (obj.isComplexGenericType(property) && depth >= 0) {
1400 final String childDbName = convertFromCamelCase(genericType);
1401 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1405 rule = edgeRules.getRule(
1406 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1407 } catch (EdgeRuleNotFoundException e) {
1408 throw new NoEdgeRuleFoundException(e);
1409 } catch (AmbiguousRuleChoiceException e) {
1410 throw new MultipleEdgeRuleFoundException(e);
1412 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1414 Direction ruleDirection = rule.getDirection();
1415 List<Vertex> verticesList = new ArrayList<>();
1416 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1417 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1418 verticesList.add(vertex);
1421 if (!verticesList.isEmpty()) {
1422 getList = obj.getValue(property);
1425 for (Vertex childVertex : verticesList) {
1426 if (!seen.contains(childVertex)) {
1427 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1430 dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1431 if (result != null && getList != null) {
1432 getList.add(argumentObject.getUnderlyingObject());
1437 LOGGER.warn("Cycle found while serializing vertex id={}",
1438 childVertex.id().toString());
1441 if (processed == 0) {
1442 // vertices were all seen, reset the list
1445 if (processed > 0) {
1449 } else if (obj.isSimpleGenericType(property)) {
1450 List<Object> temp = this.engine.getListProperty(v, property);
1452 getList = (List<Object>) obj.getValue(property);
1453 getList.addAll(temp);
1461 // no changes were made to this obj, discard the instance
1465 this.enrichData(obj, v);
1470 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1471 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1472 if (nodeType == null) {
1473 throw new AAIException("AAI_6143");
1476 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1477 Set<Vertex> seen = new HashSet<>();
1479 StopWatch.conditionalStart();
1480 this.dbToObject(obj, v, seen, depth, true, FALSE);
1481 dbTimeMsecs += StopWatch.stopIfStarted();
1486 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1487 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1490 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1491 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1492 if (nodeType == null) {
1493 throw new AAIException("AAI_6143");
1495 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1496 Set<Vertex> seen = new HashSet<>();
1497 StopWatch.conditionalStart();
1498 this.dbToObject(obj, v, seen, depth, false, FALSE);
1499 dbTimeMsecs += StopWatch.stopIfStarted();
1504 * Copy simple property.
1506 * @param property the property
1507 * @param obj the obj
1509 * @throws IllegalArgumentException the illegal argument exception
1510 * @throws SecurityException the security exception
1512 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1513 final Object temp = getProperty(obj, property, v);
1515 obj.setValue(property, temp);
1519 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1521 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1522 String[] simplePropsArray = new String[simpleProperties.size()];
1523 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1525 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1527 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1529 return simplePropsHashMap;
1532 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1533 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1534 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1535 return relationshipList;
1539 * Creates the relationship list.
1542 * @param obj the obj
1543 * @param cleanUp the clean up
1544 * @return the object
1545 * @throws IllegalArgumentException the illegal argument exception
1546 * @throws SecurityException the security exception
1547 * @throws UnsupportedEncodingException the unsupported encoding exception
1548 * @throws AAIException the AAI exception
1549 * @throws URISyntaxException
1551 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1552 throws UnsupportedEncodingException, AAIException {
1553 // default boolean value for isSkipRelatedTo is false
1554 return createRelationshipList(v, obj, cleanUp, false);
1558 * Creates the relationship list.
1561 * @param obj the obj
1562 * @param cleanUp the clean up
1563 * @param isSkipRelatedTo to determine adding related-to-property in response
1564 * @return the object
1565 * @throws IllegalArgumentException the illegal argument exception
1566 * @throws SecurityException the security exception
1567 * @throws UnsupportedEncodingException the unsupported encoding exception
1568 * @throws AAIException the AAI exception
1569 * @throws URISyntaxException
1571 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1572 throws UnsupportedEncodingException, AAIException {
1574 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1575 VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1577 if (!nodeTypeProperty.isPresent()) {
1578 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1582 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1584 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1586 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1588 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1589 .edgeType(EdgeType.COUSIN)
1590 .version(obj.getVersion());
1592 for (Path path : paths) {
1593 if (path.size() < 3) {
1598 // v ----related-to--> otherV
1599 // In the above case,
1600 // path objects get(0) returns vertex v
1601 // path objects.get(1) returns edge related-to
1602 // path objects.get(2) returns vertex otherV
1603 Edge edge = path.get(1);
1604 Vertex otherV = path.get(2);
1606 // TODO: Come back and revisit this code
1607 // Create a query based on the a nodetype and b nodetype
1608 // which is also a cousin edge and ensure the version
1609 // is used properly so for example in order to be backwards
1610 // compatible if we had allowed a edge between a and b
1611 // in a previous release and we decided to remove it from
1612 // the edge rules in the future we can display the edge
1613 // only for the older apis and the new apis if the edge rule
1614 // is removed will not be seen in the newer version of the API
1617 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1618 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1623 String edgeLabel = edge.label();
1624 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1626 if (!edgeIngestor.hasRule(ruleQuery)) {
1627 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1631 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1632 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1633 if (result != null) {
1634 relationshipObjList.add(result);
1639 if (relationshipObjList.isEmpty()) {
1647 * Process edge relationship.
1649 * @param relationshipObj the relationship obj
1650 * @param edgeLabel the edge's label
1651 * @param cleanUp the clean up
1652 * @return the object
1653 * @throws IllegalArgumentException the illegal argument exception
1654 * @throws SecurityException the security exception
1655 * @throws UnsupportedEncodingException the unsupported encoding exception
1656 * @throws AAIUnknownObjectException
1657 * @throws URISyntaxException
1659 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1660 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1662 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1664 if (!aaiUriProperty.isPresent()) {
1668 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1670 URIToRelationshipObject uriParser;
1671 Introspector result;
1673 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1674 result = uriParser.getResult();
1675 } catch (AAIException | URISyntaxException e) {
1676 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1677 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1681 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1683 if (cousinVertexNodeType.isPresent()) {
1684 String cousinType = cousinVertexNodeType.value().toString();
1685 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1686 this.addRelatedToProperty(result, cousin, cousinType);
1690 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1691 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1694 return result.getUnderlyingObject();
1698 * Gets the URI for vertex.
1701 * @return the URI for vertex
1702 * @throws IllegalArgumentException the illegal argument exception
1703 * @throws SecurityException the security exception
1704 * @throws UnsupportedEncodingException the unsupported encoding exception
1705 * @throws AAIUnknownObjectException
1707 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1709 return getURIForVertex(v, false);
1712 public URI getURIForVertex(Vertex v, boolean overwrite) {
1713 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1715 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1717 if (aaiUri != null && !overwrite) {
1718 uri = UriBuilder.fromPath(aaiUri).build();
1725 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1726 throws AAIUnknownObjectException {
1731 obj = this.loader.introspectorFromName(cousinType);
1732 } catch (AAIUnknownObjectException ex) {
1733 if (LOGGER.isTraceEnabled()) {
1734 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1735 cousinType, cousinVertex.id());
1740 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1741 List<Introspector> relatedToProperties = new ArrayList<>();
1743 if (nameProps != null) {
1744 String[] props = nameProps.split(",");
1745 for (String prop : props) {
1746 final Object temp = getProperty(obj, prop, cousinVertex);
1747 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1748 relatedTo.setValue("property-key", cousinType + "." + prop);
1749 relatedTo.setValue("property-value", temp);
1750 relatedToProperties.add(relatedTo);
1754 if (!relatedToProperties.isEmpty()) {
1755 List<Object> relatedToList = relationship.getValue("related-to-property");
1756 for (Introspector introspector : relatedToProperties) {
1757 relatedToList.add(introspector.getUnderlyingObject());
1763 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1765 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1766 String dbPropertyName = prop;
1768 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1769 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1772 return vertex.<Object>property(dbPropertyName).orElse(null);
1778 * @param relationship the relationship
1779 * @param inputVertex the input vertex
1780 * @return true, if successful
1781 * @throws UnsupportedEncodingException the unsupported encoding exception
1782 * @throws AAIException the AAI exception
1784 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1785 throws UnsupportedEncodingException, AAIException {
1787 Vertex relatedVertex;
1788 StopWatch.conditionalStart();
1789 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1791 String label = null;
1792 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1793 label = relationship.getValue(RELATIONSHIP_LABEL);
1796 List<Vertex> results = parser.getQueryBuilder().toList();
1797 if (results.isEmpty()) {
1798 dbTimeMsecs += StopWatch.stopIfStarted();
1799 AAIException e = new AAIException(AAI_6129,
1800 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1801 e.getTemplateVars().add(parser.getResultType());
1802 e.getTemplateVars().add(parser.getUri().toString());
1805 // still an issue if there's more than one
1806 relatedVertex = results.get(0);
1809 if (relatedVertex != null) {
1813 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1815 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1816 if(isDeltaEventsEnabled) {
1817 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1820 // attempted to link two vertexes already linked
1823 dbTimeMsecs += StopWatch.stopIfStarted();
1827 dbTimeMsecs += StopWatch.stopIfStarted();
1828 return relatedVertex;
1832 * Gets all the edges between of the type with the specified label.
1834 * @param aVertex the out vertex
1835 * @param bVertex the in vertex
1836 * @return the edges between
1838 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1842 if (bVertex != null) {
1843 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1844 if (EdgeType.TREE.equals(type)) {
1845 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1846 if (edgeRule.getDirection().equals(Direction.IN)) {
1847 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1848 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1849 .not(__.has(EdgeField.PRIVATE.toString(), true));
1851 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1852 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1853 .not(__.has(EdgeField.PRIVATE.toString(), true));
1855 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1857 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1858 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1859 .not(__.has(EdgeField.PRIVATE.toString(), true));
1860 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1862 List<Edge> list = findEdgesBetween.toList();
1863 if (!list.isEmpty()) {
1864 result = list.get(0);
1872 * Gets all the edges string between of the type.
1874 * @param aVertex the out vertex
1875 * @param bVertex the in vertex
1876 * @return the edges between
1877 * @throws NoEdgeRuleFoundException
1879 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1881 List<String> result = new ArrayList<>();
1883 if (bVertex != null) {
1884 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1885 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1886 if (EdgeType.TREE.equals(type)) {
1887 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1888 __.has(EdgeField.PRIVATE.toString(), true)));
1890 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1891 .not(__.has(EdgeField.PRIVATE.toString(), true));
1893 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1894 result = findEdgesBetween.label().toList();
1900 * Gets all the edges between the vertexes with the label and type.
1902 * @param vertexOut the out vertex
1903 * @param vertexIn the in vertex
1905 * @return the edges between
1906 * @throws AAIException the AAI exception
1908 private Edge getEdgesBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1912 if (vertexIn != null) {
1913 String aType = vertexOut.<String>property(AAIProperties.NODE_TYPE).value();
1914 String bType = vertexIn.<String>property(AAIProperties.NODE_TYPE).value();
1915 EdgeRuleQuery query = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1918 rule = edgeRules.getRule(query);
1919 } catch (EdgeRuleNotFoundException e) {
1920 throw new NoEdgeRuleFoundException(e);
1921 } catch (AmbiguousRuleChoiceException e) {
1922 throw new MultipleEdgeRuleFoundException(e);
1924 edge = this.getEdgeBetweenWithLabel(type, vertexOut, vertexIn, rule);
1931 * Gets the edge between with the label and edge type.
1933 * @param vertexOut the out vertex
1934 * @param vertexIn the in vertex
1936 * @return the edge between
1937 * @throws AAIException the AAI exception
1938 * @throws NoEdgeRuleFoundException
1940 public Edge getEdgeBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1942 StopWatch.conditionalStart();
1943 if (vertexIn != null) {
1945 Edge edge = this.getEdgesBetween(type, vertexOut, vertexIn, label);
1947 dbTimeMsecs += StopWatch.stopIfStarted();
1952 dbTimeMsecs += StopWatch.stopIfStarted();
1956 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1957 return this.getEdgeBetween(type, aVertex, bVertex, null);
1963 * @param relationship the relationship
1964 * @param inputVertex the input vertex
1965 * @return true, if successful
1966 * @throws UnsupportedEncodingException the unsupported encoding exception
1967 * @throws AAIException the AAI exception
1969 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1970 throws UnsupportedEncodingException, AAIException {
1972 Vertex relatedVertex;
1973 StopWatch.conditionalStart();
1974 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1976 List<Vertex> results = parser.getQueryBuilder().toList();
1978 String label = null;
1979 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1980 label = relationship.getValue(RELATIONSHIP_LABEL);
1983 if (results.isEmpty()) {
1984 dbTimeMsecs += StopWatch.stopIfStarted();
1985 return Optional.empty();
1988 relatedVertex = results.get(0);
1991 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1992 } catch (NoEdgeRuleFoundException e) {
1993 dbTimeMsecs += StopWatch.stopIfStarted();
1994 throw new AAIException(AAI_6129, e);
1997 if (isDeltaEventsEnabled) {
1998 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1999 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
2002 dbTimeMsecs += StopWatch.stopIfStarted();
2003 return Optional.of(relatedVertex);
2005 dbTimeMsecs += StopWatch.stopIfStarted();
2006 return Optional.empty();
2012 * Delete with traversal.
2014 * @param startVertex the start vertex
2016 public void deleteWithTraversal(Vertex startVertex) {
2017 StopWatch.conditionalStart();
2018 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
2019 this.delete(results);
2023 * Removes the list of vertexes from the graph
2025 * Current the vertex label will just be vertex but
2026 * in the future the aai-node-type property will be replaced
2027 * by using the vertex label as when retrieving an vertex
2028 * and retrieving an single property on an vertex will pre-fetch
2029 * all the properties of that vertex and this is due to the following property
2032 * query.fast-property=true
2035 * JanusGraph doesn't provide the capability to override that
2036 * at a transaction level and there is a plan to move to vertex label
2037 * so it is best to utilize this for now and when the change is applied
2039 * @param vertices - list of vertices to delete from the graph
2041 void delete(List<Vertex> vertices) {
2042 StopWatch.conditionalStart();
2044 for (Vertex v : vertices) {
2045 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
2046 if(isDeltaEventsEnabled) {
2047 deltaForVertexDelete(v);
2049 //add the cousin vertexes of v to have their resource-version updated and notified on.
2050 v.edges(Direction.BOTH)
2051 .forEachRemaining(e -> {
2052 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
2053 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
2054 e.bothVertices().forEachRemaining(cousinV -> {
2055 if (!v.equals(cousinV)) {
2056 edgeVertexes.add(cousinV);
2062 //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
2063 // it should be removed from them as v is to be deleted
2064 edgeVertexes.remove(v);
2065 updatedVertexes.remove(v);
2069 dbTimeMsecs += StopWatch.stopIfStarted();
2072 private void deltaForVertexDelete(Vertex vertex) {
2073 String aaiUri = vertex.property(AAIProperties.AAI_URI).value().toString();
2074 vertex.keys().forEach(k -> {
2075 List<Object> list = new ArrayList<>();
2076 vertex.properties(k).forEachRemaining(vp -> list.add(vp.value()));
2077 if (list.size() == 1) {
2078 addPropDelta(aaiUri, k,
2079 PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)),
2080 DeltaAction.DELETE);
2082 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list),
2083 DeltaAction.DELETE);
2087 vertex.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
2094 * @param resourceVersion the resource version
2095 * @throws IllegalArgumentException the illegal argument exception
2096 * @throws AAIException the AAI exception
2097 * @throws InterruptedException the interrupted exception
2099 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
2100 throws IllegalArgumentException, AAIException {
2102 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2104 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
2105 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
2106 * So its better to make these in 2 steps
2108 if (result && !deletableVertices.isEmpty()) {
2109 result = verifyPreventDeleteSemantics(deletableVertices);
2114 deleteWithTraversal(v);
2115 } catch (IllegalStateException e) {
2116 throw new AAIException("AAI_6110", e);
2125 * @param vertex the vertex
2126 * @param resourceVersion the resource version
2127 * @throws IllegalArgumentException the illegal argument exception
2128 * @throws AAIException the AAI exception
2129 * @throws InterruptedException the interrupted exception
2131 public void delete(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2132 throws IllegalArgumentException, AAIException {
2134 boolean result = verifyDeleteSemantics(vertex, resourceVersion, enableResourceVersion);
2139 deleteWithTraversal(vertex);
2140 } catch (IllegalStateException e) {
2141 throw new AAIException("AAI_6110", e);
2148 * Verify delete semantics.
2150 * @param vertex the vertex
2151 * @param resourceVersion the resource version
2152 * @return true, if successful
2153 * @throws AAIException the AAI exception
2155 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2156 throws AAIException {
2159 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2160 if (enableResourceVersion) {
2161 this.verifyResourceVersion("delete", nodeType,
2162 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2164 List<Vertex> vertices = new ArrayList<>();
2165 vertices.add(vertex);
2166 result = verifyPreventDeleteSemantics(vertices);
2172 * Verify Prevent delete semantics.
2174 * @param vertices the list of vertices
2175 * @return true, if successful
2176 * @throws AAIException the AAI exception
2178 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2179 boolean result = true;
2180 String errorDetail = " unknown delete semantic found";
2181 String aaiExceptionCode = "";
2183 StopWatch.conditionalStart();
2185 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2186 * "prevent-delete" condition
2187 * If yes - that should prevent the deletion of the vertex
2188 * Dedup makes sure we dont capture the prevent-delete vertices twice
2189 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2192 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2193 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2194 .values(AAIProperties.NODE_TYPE),
2195 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2196 .values(AAIProperties.NODE_TYPE))
2199 dbTimeMsecs += StopWatch.stopIfStarted();
2200 if (!preventDeleteVertices.isEmpty()) {
2201 aaiExceptionCode = "AAI_6110";
2202 errorDetail = String.format(
2203 "Object is being reference by additional objects preventing it from being deleted." +
2204 " Please clean up references from the following types %s",
2205 preventDeleteVertices);
2209 throw new AAIException(aaiExceptionCode, errorDetail);
2215 * Verify resource version.
2217 * @param action the action
2218 * @param nodeType the node type
2219 * @param currentResourceVersion the current resource version
2220 * @param resourceVersion the resource version
2221 * @param uri the uri
2222 * @return true, if successful
2223 * @throws AAIException the AAI exception
2225 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2226 String resourceVersion, String uri) throws AAIException {
2227 String enabled = "";
2228 String errorDetail = "";
2229 String aaiExceptionCode = "";
2230 boolean isDeleteResourceVersionOk = true;
2231 if (currentResourceVersion == null) {
2232 currentResourceVersion = "";
2235 if (resourceVersion == null) {
2236 resourceVersion = "";
2239 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2241 } catch (AAIException e) {
2242 ErrorLogHelper.logException(e);
2244 if (enabled.equals("true")) {
2245 if ("delete".equals(action)) {
2246 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2248 if ((!isDeleteResourceVersionOk)
2249 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2250 if ("create".equals(action) && !resourceVersion.equals("")) {
2251 errorDetail = "resource-version passed for " + action + " of " + uri;
2252 aaiExceptionCode = "AAI_6135";
2253 } else if (resourceVersion.equals("")) {
2254 errorDetail = "resource-version not passed for " + action + " of " + uri;
2255 aaiExceptionCode = "AAI_6130";
2257 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2258 aaiExceptionCode = "AAI_6131";
2261 throw new AAIException(aaiExceptionCode, errorDetail);
2269 * Verify resource version for delete.
2271 * @param currentResourceVersion the current resource version
2272 * @param resourceVersion the resource version
2273 * @return true, if successful or false if there is a mismatch
2275 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2277 boolean isDeleteResourceVersionOk = true;
2278 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2279 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2281 if ((!currentResourceVersion.equals(resourceVersion))
2282 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2283 isDeleteResourceVersionOk = false;
2285 return isDeleteResourceVersionOk;
2289 * Convert from camel case.
2291 * @param name the name
2292 * @return the string
2294 private String convertFromCamelCase(String name) {
2296 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2298 NamingExceptions exceptions = NamingExceptions.getInstance();
2299 result = exceptions.getDBName(result);
2304 private boolean canModify(Introspector obj, String propName, String requestContext) {
2305 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2306 if (readOnly != null) {
2307 final String[] items = readOnly.split(",");
2308 for (String item : items) {
2309 if (requestContext.equals(item)) {
2317 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2319 SideEffectRunner.Builder runnerBuilder =
2320 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class)
2321 .addSideEffect(PrivateEdge.class);
2322 if (isMultiTenancyEnabled) {
2323 runnerBuilder.addSideEffect(OwnerCheck.class);
2325 runnerBuilder.build().execute(obj, self);
2328 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2330 SideEffectRunner runner =
2331 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2333 runner.execute(obj, self);
2336 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2338 SideEffectRunner.Builder runnerBuilder =
2339 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class);
2341 if (isMultiTenancyEnabled) {
2342 runnerBuilder.addSideEffect(OwnerCheck.class);
2344 runnerBuilder.build().execute(obj, self);
2347 public double getDBTimeMsecs() {
2348 return (dbTimeMsecs);
2352 * Db to object With Filters
2353 * This is for a one-time run with Tenant Isloation to only filter relationships
2355 * @param obj the obj
2356 * @param vertex the vertex from the graph
2357 * @param depth the depth
2358 * @param nodeOnly specify if to exclude relationships or not
2359 * @param filterCousinNodes
2360 * @return the introspector
2361 * @throws AAIException the AAI exception
2362 * @throws IllegalAccessException the illegal access exception
2363 * @throws IllegalArgumentException the illegal argument exception
2364 * @throws InvocationTargetException the invocation target exception
2365 * @throws SecurityException the security exception
2366 * @throws InstantiationException the instantiation exception
2367 * @throws NoSuchMethodException the no such method exception
2368 * @throws UnsupportedEncodingException the unsupported encoding exception
2369 * @throws MalformedURLException the malformed URL exception
2370 * @throws AAIUnknownObjectException
2371 * @throws URISyntaxException
2373 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertex, Set<Vertex> seen, int depth, boolean nodeOnly,
2374 List<String> filterCousinNodes, List<String> filterParentNodes)
2375 throws AAIException, UnsupportedEncodingException {
2376 return dbToObjectWithFilters(obj, vertex, seen, depth, nodeOnly,
2377 filterCousinNodes, filterParentNodes, false);
2381 * Db to object With Filters
2382 * This is for a one-time run with Tenant Isloation to only filter relationships
2383 * TODO: Chnage the original dbToObject to take filter parent/cousins
2385 * @param obj the obj
2386 * @param vertexParam the vertex from the graph
2387 * @param depth the depth
2388 * @param nodeOnly specify if to exclude relationships or not
2389 * @param filterCousinNodes
2390 * @param isSkipRelatedTo determine to incorporated related-to-property data
2391 * @return the introspector
2392 * @throws AAIException the AAI exception
2393 * @throws IllegalAccessException the illegal access exception
2394 * @throws IllegalArgumentException the illegal argument exception
2395 * @throws InvocationTargetException the invocation target exception
2396 * @throws SecurityException the security exception
2397 * @throws InstantiationException the instantiation exception
2398 * @throws NoSuchMethodException the no such method exception
2399 * @throws UnsupportedEncodingException the unsupported encoding exception
2400 * @throws MalformedURLException the malformed URL exception
2401 * @throws AAIUnknownObjectException
2402 * @throws URISyntaxException
2404 // TODO - See if you can merge the 2 dbToObjectWithFilters
2405 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertexParam, Set<Vertex> seen, int depth, boolean nodeOnly,
2406 List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2407 throws AAIException, UnsupportedEncodingException {
2408 String cleanUp = FALSE;
2413 seen.add(vertexParam);
2414 boolean modified = false;
2415 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2416 List<Object> getList = null;
2418 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2419 this.copySimpleProperty(property, obj, vertexParam);
2422 if (obj.isComplexType(property)) {
2423 /* container case */
2425 if (!property.equals("relationship-list") && depth >= 0) {
2426 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2427 Object result = dbToObjectWithFilters(argumentObject, vertexParam, seen, depth + 1, nodeOnly,
2428 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2429 if (result != null) {
2430 obj.setValue(property, argumentObject.getUnderlyingObject());
2433 } else if (property.equals("relationship-list") && !nodeOnly) {
2434 /* relationships need to be handled correctly */
2435 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2437 createFilteredRelationshipList(vertexParam, relationshipList, cleanUp,
2438 filterCousinNodes, isSkipRelatedTo);
2439 if (relationshipList != null) {
2440 obj.setValue(property, relationshipList.getUnderlyingObject());
2445 } else if (obj.isListType(property)) {
2447 if (property.equals("any")) {
2450 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2451 if (obj.isComplexGenericType(property) && depth >= 0) {
2452 final String childDbName = convertFromCamelCase(genericType);
2453 String vertexType = vertexParam.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2456 boolean isThisParentRequired =
2457 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2459 EdgeRuleQuery query = new EdgeRuleQuery.Builder(vertexType, childDbName).edgeType(EdgeType.TREE).build();
2462 rule = edgeRules.getRule(query);
2463 } catch (EdgeRuleNotFoundException e) {
2464 throw new NoEdgeRuleFoundException(e);
2465 } catch (AmbiguousRuleChoiceException e) {
2466 throw new MultipleEdgeRuleFoundException(e);
2468 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2469 Direction ruleDirection = rule.getDirection();
2470 List<Vertex> verticesList = new ArrayList<>();
2471 vertexParam.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2472 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2473 verticesList.add(vertex);
2476 if (!verticesList.isEmpty()) {
2477 getList = obj.getValue(property);
2480 for (Vertex childVertex : verticesList) {
2481 if (!seen.contains(childVertex)) {
2482 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2484 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2485 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2486 if (result != null && getList != null) {
2487 getList.add(argumentObject.getUnderlyingObject());
2492 LOGGER.warn("Cycle found while serializing vertex id={}",
2493 childVertex.id().toString());
2496 if (processed == 0) {
2497 // vertices were all seen, reset the list
2500 if (processed > 0) {
2504 } else if (obj.isSimpleGenericType(property)) {
2505 List<Object> temp = this.engine.getListProperty(vertexParam, property);
2507 getList = obj.getValue(property);
2508 getList.addAll(temp);
2519 // no changes were made to this obj, discard the instance
2523 this.enrichData(obj, vertexParam);
2529 * Creates the relationship list with the filtered node types.
2531 * @param vertex the vertex
2532 * @param obj the obj
2533 * @param cleanUp the clean up
2534 * @return the object
2535 * @throws IllegalArgumentException the illegal argument exception
2536 * @throws SecurityException the security exception
2537 * @throws UnsupportedEncodingException the unsupported encoding exception
2538 * @throws AAIException the AAI exception
2540 private Introspector createFilteredRelationshipList(Vertex vertex, Introspector obj, String cleanUp,
2541 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2542 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(vertex);
2544 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2545 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2546 return filterNodes.parallelStream().anyMatch(node::contains);
2549 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2551 List<Vertex> cousins = new ArrayList<>();
2552 cousinVertices.forEachRemaining(cousins::add);
2553 for (Vertex cousin : cousins) {
2555 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2556 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2557 if (result != null) {
2558 relationshipObjList.add(result);
2563 if (relationshipObjList.isEmpty()) {
2570 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2571 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2572 return this.edgeVertexes;
2575 public void addVertexToEdgeVertexes(Vertex vertex) {
2576 this.edgeVertexes.add(vertex);
2579 private String urlToUri(String url) {
2580 if (url.startsWith("/")) {
2581 url = url.substring(1);
2584 if (url.endsWith("/")) {
2585 url = url.substring(0, url.length() - 1);
2588 // TODO - Check if this makes to do for model driven for base uri path
2589 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2590 if (url.charAt(0) != '/') {