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 java.io.UnsupportedEncodingException;
23 import java.lang.reflect.Array;
24 import java.lang.reflect.InvocationTargetException;
25 import java.net.MalformedURLException;
27 import java.net.URISyntaxException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.LinkedHashMap;
35 import java.util.LinkedHashSet;
36 import java.util.List;
38 import java.util.Optional;
40 import java.util.UUID;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.ExecutorService;
43 import java.util.concurrent.Future;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
47 import javax.ws.rs.core.UriBuilder;
49 import org.apache.commons.lang3.StringUtils;
50 import org.apache.tinkerpop.gremlin.process.traversal.Path;
51 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
52 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
53 import org.apache.tinkerpop.gremlin.structure.Direction;
54 import org.apache.tinkerpop.gremlin.structure.Edge;
55 import org.apache.tinkerpop.gremlin.structure.Vertex;
56 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
57 import org.janusgraph.core.SchemaViolationException;
58 import org.javatuples.Pair;
59 import org.onap.aai.concurrent.AaiCallable;
60 import org.onap.aai.config.SpringContextAware;
61 import org.onap.aai.db.props.AAIProperties;
62 import org.onap.aai.edges.EdgeIngestor;
63 import org.onap.aai.edges.EdgeRule;
64 import org.onap.aai.edges.EdgeRuleQuery;
65 import org.onap.aai.edges.enums.AAIDirection;
66 import org.onap.aai.edges.enums.EdgeField;
67 import org.onap.aai.edges.enums.EdgeProperty;
68 import org.onap.aai.edges.enums.EdgeType;
69 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
70 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
71 import org.onap.aai.exceptions.AAIException;
72 import org.onap.aai.introspection.Introspector;
73 import org.onap.aai.introspection.IntrospectorFactory;
74 import org.onap.aai.introspection.Loader;
75 import org.onap.aai.introspection.LoaderFactory;
76 import org.onap.aai.introspection.ModelType;
77 import org.onap.aai.introspection.PropertyPredicates;
78 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
79 import org.onap.aai.introspection.sideeffect.DataCopy;
80 import org.onap.aai.introspection.sideeffect.DataLinkReader;
81 import org.onap.aai.introspection.sideeffect.DataLinkWriter;
82 import org.onap.aai.introspection.sideeffect.OwnerCheck;
83 import org.onap.aai.introspection.sideeffect.PrivateEdge;
84 import org.onap.aai.introspection.sideeffect.SideEffectRunner;
85 import org.onap.aai.logging.ErrorLogHelper;
86 import org.onap.aai.logging.LogFormatTools;
87 import org.onap.aai.logging.StopWatch;
88 import org.onap.aai.parsers.query.QueryParser;
89 import org.onap.aai.parsers.relationship.RelationshipToURI;
90 import org.onap.aai.parsers.uri.URIParser;
91 import org.onap.aai.parsers.uri.URIToObject;
92 import org.onap.aai.parsers.uri.URIToRelationshipObject;
93 import org.onap.aai.query.builder.QueryBuilder;
94 import org.onap.aai.schema.enums.ObjectMetadata;
95 import org.onap.aai.schema.enums.PropertyMetadata;
96 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
97 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
98 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
99 import org.onap.aai.serialization.engines.query.QueryEngine;
100 import org.onap.aai.setup.SchemaVersion;
101 import org.onap.aai.setup.SchemaVersions;
102 import org.onap.aai.util.AAIConfig;
103 import org.onap.aai.util.AAIConstants;
104 import org.onap.aai.util.delta.DeltaAction;
105 import org.onap.aai.util.delta.ObjectDelta;
106 import org.onap.aai.util.delta.PropertyDelta;
107 import org.onap.aai.util.delta.PropertyDeltaFactory;
108 import org.onap.aai.util.delta.RelationshipDelta;
109 import org.onap.aai.workarounds.NamingExceptions;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
112 import org.springframework.context.ApplicationContext;
114 import com.google.common.base.CaseFormat;
116 public class DBSerializer {
118 private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class);
119 private static final String RELATIONSHIP_LABEL = "relationship-label";
120 private static final String RELATIONSHIP = "relationship";
121 public static final String FALSE = "false";
122 public static final String AAI_6145 = "AAI_6145";
123 public static final String AAI_6129 = "AAI_6129";
125 private final TransactionalGraphEngine engine;
126 private final String sourceOfTruth;
127 private final Set<String> groups;
128 private final ModelType introspectionType;
129 private final SchemaVersion version;
130 private final Loader latestLoader;
131 private EdgeSerializer edgeSer;
132 private EdgeIngestor edgeRules;
133 private final Loader loader;
134 private final String baseURL;
135 private double dbTimeMsecs = 0;
136 private long currentTimeMillis;
138 private SchemaVersions schemaVersions;
139 private Set<String> namedPropNodes;
140 private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
141 private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
142 private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
143 private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair = new LinkedHashMap<>();
144 private int notificationDepth;
145 private boolean isDeltaEventsEnabled;
146 private boolean isMultiTenancyEnabled;
149 * Instantiates a new DB serializer.
151 * @param version the version
152 * @param engine the engine
153 * @param introspectionType the introspection type
154 * @param sourceOfTruth the source of truth
155 * @throws AAIException
157 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
158 String sourceOfTruth) throws AAIException {
159 this.engine = engine;
160 this.sourceOfTruth = sourceOfTruth;
161 this.groups = Collections.EMPTY_SET;
162 this.introspectionType = introspectionType;
163 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
164 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
166 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
167 this.version = version;
169 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
170 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
171 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
172 this.currentTimeMillis = System.currentTimeMillis();
173 // If creating the DBSerializer the old way then set the notification depth to maximum
174 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
178 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
179 String sourceOfTruth, Set<String> groups) throws AAIException {
180 this.engine = engine;
181 this.sourceOfTruth = sourceOfTruth;
182 this.groups = groups;
183 this.introspectionType = introspectionType;
184 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
185 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
187 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
188 this.version = version;
190 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
191 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
192 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
193 this.currentTimeMillis = System.currentTimeMillis();
194 // If creating the DBSerializer the old way then set the notification depth to maximum
195 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
199 public DBSerializer(SchemaVersion version,
200 TransactionalGraphEngine engine,
201 ModelType introspectionType,
202 String sourceOfTruth,
203 int notificationDepth) throws AAIException {
204 this.engine = engine;
205 this.sourceOfTruth = sourceOfTruth;
206 this.groups = Collections.EMPTY_SET;
207 this.introspectionType = introspectionType;
208 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
209 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
211 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
212 this.version = version;
214 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
215 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
216 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
217 this.currentTimeMillis = System.currentTimeMillis();
218 this.notificationDepth = notificationDepth;
222 public DBSerializer(SchemaVersion version,
223 TransactionalGraphEngine engine,
224 ModelType introspectionType,
225 String sourceOfTruth,
227 int notificationDepth) throws AAIException {
228 this.engine = engine;
229 this.sourceOfTruth = sourceOfTruth;
230 this.groups = groups;
231 this.introspectionType = introspectionType;
232 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
233 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
235 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
236 this.version = version;
238 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
239 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
240 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
241 this.currentTimeMillis = System.currentTimeMillis();
242 this.notificationDepth = notificationDepth;
246 public DBSerializer(SchemaVersion version,
247 TransactionalGraphEngine engine,
248 ModelType introspectionType,
249 String sourceOfTruth,
250 int notificationDepth,
251 String serverBase) throws AAIException {
252 this.engine = engine;
253 this.sourceOfTruth = sourceOfTruth;
254 this.groups = Collections.EMPTY_SET;
255 this.introspectionType = introspectionType;
256 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
257 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
259 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
260 this.version = version;
262 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
263 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
264 this.baseURL = serverBase;
265 this.currentTimeMillis = System.currentTimeMillis();
266 this.notificationDepth = notificationDepth;
270 public DBSerializer(SchemaVersion version,
271 TransactionalGraphEngine engine,
272 ModelType introspectionType,
273 String sourceOfTruth,
275 int notificationDepth,
276 String serverBase) throws AAIException {
277 this.engine = engine;
278 this.sourceOfTruth = sourceOfTruth;
279 this.groups = groups;
280 this.introspectionType = introspectionType;
281 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
282 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
284 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, latestVersion);
285 this.version = version;
287 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
288 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
289 this.baseURL = serverBase;
290 this.currentTimeMillis = System.currentTimeMillis();
291 this.notificationDepth = notificationDepth;
295 private void initBeans() {
296 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
297 ApplicationContext ctx = SpringContextAware.getApplicationContext();
298 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
300 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
301 setEdgeSerializer(es);
302 isDeltaEventsEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
303 isMultiTenancyEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment().getProperty("multi.tenancy.enabled", FALSE));
306 public void setEdgeSerializer(EdgeSerializer edgeSer) {
307 this.edgeSer = edgeSer;
310 public EdgeSerializer getEdgeSeriailizer() {
314 public void setEdgeIngestor(EdgeIngestor ei) {
318 public EdgeIngestor getEdgeIngestor() {
319 return this.edgeRules;
322 public Map<Vertex, Boolean> getUpdatedVertexes() {
323 return updatedVertexes;
326 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair() {
327 return impliedDeleteUriObjectPair;
330 public Set<String> getGroups() {
335 * Touch standard vertex properties.
337 * @param isNewVertex the is new vertex
339 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
340 String timeNowInSec = Long.toString(currentTimeMillis);
342 String uuid = UUID.randomUUID().toString();
343 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
344 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
345 v.property(AAIProperties.AAI_UUID, uuid);
346 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
347 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
348 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
350 if(isDeltaEventsEnabled) {
351 standardVertexPropsDeltas(v, timeNowInSec);
353 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
354 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
355 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
359 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
360 String uri = v.property(AAIProperties.AAI_URI).value().toString();
361 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
362 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
363 if (getObjectDeltas().containsKey(uri)) {
364 getObjectDeltas().get(uri).setAction(objDeltaAction);
367 addPropDelta(uri, AAIProperties.AAI_UUID, PropertyDeltaFactory
368 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()),
370 addPropDelta(uri, AAIProperties.NODE_TYPE, PropertyDeltaFactory
371 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()),
373 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH, PropertyDeltaFactory
374 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()),
376 addPropDelta(uri, AAIProperties.CREATED_TS, PropertyDeltaFactory
377 .getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()),
380 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
383 AAIProperties.RESOURCE_VERSION,
384 PropertyDeltaFactory.getDelta(objDeltaAction, timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()),
389 AAIProperties.LAST_MOD_TS,
390 PropertyDeltaFactory.getDelta(objDeltaAction, currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()),
395 AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
396 PropertyDeltaFactory.getDelta(objDeltaAction, this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
400 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
401 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
402 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
406 public Map<String, ObjectDelta> getObjectDeltas() {
410 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
411 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
412 objectDelta.addPropertyDelta(prop, delta);
413 objectDeltas.put(uri, objectDelta);
416 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
417 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri, new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
418 objectDelta.addRelationshipDelta(delta);
419 objectDeltas.put(uri, objectDelta);
422 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
423 v.property(AAIProperties.NODE_TYPE, nodeType);
424 touchStandardVertexProperties(v, isNewVertex);
428 * Creates the new vertex.
430 * @param wrappedObject the wrapped object
433 public Vertex createNewVertex(Introspector wrappedObject) {
436 StopWatch.conditionalStart();
437 v = this.engine.tx().addVertex(wrappedObject.getDbName());
438 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
440 dbTimeMsecs += StopWatch.stopIfStarted();
448 * @param className the class name
452 * Removes the classpath from a class name
454 public String trimClassName(String className) {
455 String returnValue = "";
457 if (className.lastIndexOf('.') == -1) {
460 returnValue = className.substring(className.lastIndexOf('.') + 1);
470 * @param uriQuery the uri query
471 * @param identifier the identifier
472 * @throws SecurityException the security exception
473 * @throws IllegalArgumentException the illegal argument exception
474 * @throws AAIException the AAI exception
475 * @throws UnsupportedEncodingException the unsupported encoding exception
477 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
478 String requestContext) throws AAIException, UnsupportedEncodingException {
479 StopWatch.conditionalStart();
481 if (uriQuery.isDependent()) {
482 // try to find the parent
483 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
484 if (!vertices.isEmpty()) {
485 Vertex parent = vertices.get(0);
486 this.reflectDependentVertex(parent, v, obj, requestContext);
488 dbTimeMsecs += StopWatch.stopIfStarted();
489 throw new AAIException("AAI_6114",
490 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
493 serializeSingleVertex(v, obj, requestContext);
496 } catch (SchemaViolationException e) {
497 dbTimeMsecs += StopWatch.stopIfStarted();
498 throw new AAIException("AAI_6117", e);
500 dbTimeMsecs += StopWatch.stopIfStarted();
503 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
504 throws UnsupportedEncodingException, AAIException {
505 StopWatch.conditionalStart();
507 boolean isTopLevel = obj.isTopLevel();
509 addUriIfNeeded(v, obj.getURI());
512 URI uri = this.getURIForVertex(v);
513 URIParser parser = new URIParser(this.loader, uri);
514 if (parser.validate()) {
515 addUriIfNeeded(v, uri.toString());
518 processObject(obj, v, requestContext);
519 } catch (SchemaViolationException e) {
520 throw new AAIException("AAI_6117", e);
522 dbTimeMsecs += StopWatch.stopIfStarted();
526 private void addUriIfNeeded(Vertex v, String uri) {
527 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
528 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
529 v.property(AAIProperties.AAI_URI, uri);
539 * @throws IllegalArgumentException the illegal argument exception
540 * @throws SecurityException the security exception
541 * @throws AAIException the AAI exception
542 * @throws UnsupportedEncodingException the unsupported encoding exception
545 * Helper method for reflectToDb
546 * Handles all the property setting
548 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
549 throws UnsupportedEncodingException, AAIException {
550 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
551 properties.remove(AAIProperties.RESOURCE_VERSION);
552 List<Vertex> dependentVertexes = new ArrayList<>();
553 List<Vertex> processedVertexes = new ArrayList<>();
555 boolean isComplexType ;
558 // If the notification depth is set to maximum
559 // this is the behavior of the expected clients
560 if(notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
561 if (!obj.isContainer()) {
562 this.touchStandardVertexProperties(v, false);
565 this.executePreSideEffects(obj, v);
566 for (String property : properties) {
567 final String propertyType;
568 propertyType = obj.getType(property);
569 isComplexType = obj.isComplexType(property);
570 isListType = obj.isListType(property);
571 Object value = obj.getValue(property);
573 if (!(isComplexType || isListType)) {
574 boolean canModify = this.canModify(obj, property, requestContext);
577 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
578 String dbProperty = property;
579 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
580 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
582 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
583 // data linked properties are ephemeral
584 // they are populated dynamically on GETs
587 Object oldValue = v.property(dbProperty).orElse(null);
588 String uri = getURIForVertex(v).toString();
590 if (!value.equals(oldValue)) {
591 if (propertyType.toLowerCase().contains(".long")) {
592 v.property(dbProperty, new Integer(((Long) value).toString()));
594 v.property(dbProperty, value);
596 if (isDeltaEventsEnabled) {
597 createDeltaProperty(uri, value, dbProperty, oldValue);
599 this.updatedVertexes.putIfAbsent(v, false);
602 if (oldValue != null) {
603 v.property(dbProperty).remove();
604 if (isDeltaEventsEnabled) {
605 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue), DeltaAction.UPDATE);
607 this.updatedVertexes.putIfAbsent(v, false);
611 } else if (isListType) {
612 List<Object> list = (List<Object>) value;
613 if (obj.isComplexGenericType(property)) {
615 for (Object o : list) {
616 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
617 child.setURIChain(obj.getURI());
618 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
623 if (isDeltaEventsEnabled) {
624 String uri = getURIForVertex(v).toString();
625 List<Object> oldVal = engine.getListProperty(v, property);
626 engine.setListProperty(v, property, list);
627 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
628 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
629 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal), DeltaAction.UPDATE);
631 } else { // is either a create or update and is handled by the called method
632 createDeltaProperty(uri, list, property, oldVal);
635 engine.setListProperty(v, property, list);
637 this.updatedVertexes.putIfAbsent(v, false);
640 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
641 // back to this method
642 if (value != null) { // effectively ignore complex properties not included in the object we're
644 if (value.getClass().isArray()) {
646 int length = Array.getLength(value);
647 for (int i = 0; i < length; i++) {
648 Object arrayElement = Array.get(value, i);
649 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
650 child.setURIChain(obj.getURI());
651 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
654 } else if (!property.equals("relationship-list")) {
656 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
657 if (introspector.isContainer()) {
658 dependentVertexes.addAll(
659 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
660 introspector.setURIChain(obj.getURI());
662 processedVertexes.addAll(processObject(introspector, v, requestContext));
665 dependentVertexes.addAll(
666 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
667 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
670 } else if (property.equals("relationship-list")) {
671 handleRelationships(obj, v);
676 this.writeThroughDefaults(v, obj);
677 /* handle those vertexes not touched */
678 for (Vertex toBeKept : processedVertexes) {
679 dependentVertexes.remove(toBeKept);
682 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
683 List<Vertex> impliedDeleteVertices = impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
685 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
686 for (Vertex curVertex : impliedDeleteVertices) {
687 if (!curVertex.property("aai-uri").isPresent()) {
688 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
691 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
692 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
694 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
696 if (!curObj.isTopLevel()) {
697 curObjRelated.putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
700 if (!impliedDeleteUriObjectPair.containsKey(curAaiUri)) {
701 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
706 impliedDelete.delete(impliedDeleteVertices);
708 // touch svp using vertex list for what changed
709 // if the notification depth is zero
710 if(notificationDepth == AAIProperties.MINIMUM_DEPTH){
711 this.updatedVertexes.entrySet().stream()
712 .filter(e -> !e.getValue())
713 .filter(e -> !edgeVertexes.contains(e.getKey()))
715 this.touchStandardVertexProperties(e.getKey(), false);
719 this.executePostSideEffects(obj, v);
720 return processedVertexes;
723 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
724 if (oldValue == null) {
725 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
727 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue), DeltaAction.UPDATE);
731 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v,
732 Introspector obj, Loader loader) throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
734 HashMap<String, Introspector> relatedVertices = new HashMap<>();
735 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
737 if (!aaiUriProperty.isPresent()) {
738 if (LOGGER.isDebugEnabled()) {
739 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
743 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
745 return relatedVertices;
748 String aaiUri = aaiUriProperty.value().toString();
750 if (!obj.isTopLevel()) {
751 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
752 List<Vertex> vertexChain;
753 // If the uriList is null then there is something wrong with converting the uri
754 // into a list of aai-uris so falling back to the old mechanism for finding parents
755 if (uriList == null) {
757 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
758 vertexChain = queryEngine.findParents(v);
759 } else if (uriList.length == 1) {
760 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
761 vertexChain = Collections.singletonList(v);
763 // the uriList at element 0 is the node in question and should not be included in the vertex chain lookup.
764 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
765 // inject v into start of vertexChain
766 vertexChain.add(0, v);
768 for (Vertex vertex : vertexChain) {
770 final Introspector vertexObj = this.getVertexProperties(vertex);
771 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
772 } catch (AAIUnknownObjectException e) {
773 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
778 final Introspector vertexObj = this.getVertexProperties(v);
779 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
780 } catch (AAIUnknownObjectException e) {
781 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
785 return relatedVertices;
789 * Given an uri, introspector object and loader object
790 * it will check if the obj is top level object if it is,
791 * it will return immediately returning the uri passed in
792 * If it isn't, it will go through, get the uriTemplate
793 * from the introspector object and get the count of "/"s
794 * and remove that part of the uri using substring
795 * and keep doing that until the current object is top level
796 * Also added the max depth just so worst case scenario
797 * Then keep adding aai-uri to the list include the aai-uri passed in
798 * Convert that list into an array and return it
805 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
807 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
808 * it converts to /vservers/vserver
810 * lastIndexOf /vservers/vserver in
811 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
816 * Use substring to get the string from 0 to that lastIndexOf
817 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
819 * From this new aai-uri, generate a introspector from the URITOObject class
820 * and keep doing this until you
824 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
825 * @param obj - introspector object of the given starting vertex
826 * @param loader - Type of loader which will always be MoxyLoader to support model driven
827 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
828 * @throws UnsupportedEncodingException
829 * @throws AAIException
831 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
832 throws UnsupportedEncodingException, AAIException {
834 List<String> uriList = new ArrayList<>();
836 String truncatedUri = aaiUri;
837 int depth = AAIProperties.MAXIMUM_DEPTH;
838 uriList.add(truncatedUri);
840 while (depth >= 0 && !obj.isTopLevel()) {
841 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
843 if (template == null) {
844 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
848 int templateCount = StringUtils.countMatches(template, "/");
849 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
851 if (templateCount > truncatedUriCount) {
852 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
856 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
857 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
858 uriList.add(truncatedUri);
859 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
863 return uriList.toArray(new String[0]);
867 * Handle relationships.
870 * @param vertex the vertex
871 * @throws SecurityException the security exception
872 * @throws IllegalArgumentException the illegal argument exception
873 * @throws UnsupportedEncodingException the unsupported encoding exception
874 * @throws AAIException the AAI exception
877 * Handles the explicit relationships defined for an obj
879 private void handleRelationships(Introspector obj, Vertex vertex)
880 throws UnsupportedEncodingException, AAIException {
882 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
883 processRelationshipList(wrappedRl, vertex);
888 * Process relationship list.
890 * @param wrapped the wrapped
892 * @throws UnsupportedEncodingException the unsupported encoding exception
893 * @throws AAIException the AAI exception
895 private void processRelationshipList(Introspector wrapped, Vertex v)
896 throws UnsupportedEncodingException, AAIException {
898 List<Object> relationships = wrapped.getValue("relationship");
899 String mainUri = getURIForVertex(v).toString();
900 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
901 EdgeRuleQuery.Builder cousinQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
902 .edgeType(EdgeType.COUSIN)
903 .version(wrapped.getVersion());
904 EdgeRuleQuery.Builder treeQueryBuilder = new EdgeRuleQuery.Builder(aNodeType)
905 .edgeType(EdgeType.TREE)
906 .version(wrapped.getVersion());
908 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
910 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
911 for (Object relationship : relationships) {
914 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
915 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
917 if (relUri.startsWith("/vnf/")) {
918 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
919 List<Vertex> results = parser.getQueryBuilder().toList();
920 if (results.isEmpty()) {
921 final AAIException ex = new AAIException(AAI_6129,
922 String.format("Node of type %s. Could not find object at: %s", parser.getResultType(), parser.getUri()));
923 ex.getTemplateVars().add(parser.getResultType());
924 ex.getTemplateVars().add(parser.getUri().toString());
927 // still an issue if there's more than one
928 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
929 relUri = results.get(0).value(AAIProperties.AAI_URI);
931 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri", results.get(0).id());
937 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
938 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
940 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
941 String bNodeType = uriToObject.getEntityName();
942 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
943 if (!edgeIngestor.hasRule(ruleQuery)) {
944 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
945 if (edgeIngestor.hasRule(treeQuery)) {
946 throw new AAIException(AAI_6145); //attempted to create cousin edge for a parent-child edge rule
948 throw new AAIException("AAI_6120", String.format(
949 "No EdgeRule found for passed nodeTypes: %s, %s.",
950 aNodeType, bNodeType));
953 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
954 if (rules.size() == 1) {
955 label = rules.get(0).getLabel();
958 defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
959 if (defaultRule.isPresent()) {
960 label = defaultRule.get().getLabel();
962 throw new AAIException(AAI_6145);
965 } catch (EdgeRuleNotFoundException ea) {
966 throw new AAIException(AAI_6145, ea);
970 cousinUriAndLabels.add(Pair.with(relUri, label));
973 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
974 Set<Path> toRemove = new HashSet<>();
977 // for each path 3 things can happen:
978 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
979 // 2. The edge rule exits in this version it's included in the request the edge is left alone
980 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
981 for (Path path : paths) {
982 if (path.size() < 3) {
987 // v ----related-to--> otherV
988 // In the above case,
989 // path objects get(0) returns vertex v
990 // path objects.get(1) returns edge related-to
991 // path objects.get(2) returns vertex otherV
992 Vertex otherV = path.get(2);
995 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
996 bUri = otherV.value(AAIProperties.AAI_URI);
1000 String edgeLabel = path.<Edge>get(1).label();
1002 Pair<String, String> key = Pair.with(bUri, edgeLabel);
1003 if (cousinUriAndLabels.contains(key)) {
1004 cousinUriAndLabels.remove(key);
1007 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1008 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
1012 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
1013 if (edgeIngestor.hasRule(ruleQuery)) {
1020 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
1021 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
1023 Vertex cousinVertex;
1024 String label = cousinUriAndLabel.getValue1();
1025 String cousinUri = cousinUriAndLabel.getValue0();
1026 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
1028 List<Vertex> results = parser.getQueryBuilder().toList();
1029 if (results.isEmpty()) {
1030 final AAIException ex = new AAIException(AAI_6129,
1031 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1032 ex.getTemplateVars().add(parser.getResultType());
1033 ex.getTemplateVars().add(parser.getUri().toString());
1036 // still an issue if there's more than one
1037 cousinVertex = results.get(0);
1040 if (cousinVertex != null) {
1041 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
1042 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
1043 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
1045 if (!edgeRules.hasRule(baseQ.build())) {
1046 throw new AAIException("AAI_6120", String.format(
1047 "No EdgeRule found for passed nodeTypes: %s, %s%s.",
1048 aNodeType, cousinType, label != null ? (" with label " + label) : ""));
1049 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
1050 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
1051 throw new AAIException(AAI_6145);
1054 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
1057 toBeCreated.add(Pair.with(cousinVertex, label));
1062 for (Path path : toRemove) {
1063 if(isDeltaEventsEnabled) {
1064 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1066 this.updatedVertexes.putIfAbsent(v, false);
1067 this.edgeVertexes.add(path.get(2));
1068 path.<Edge>get(1).remove();
1071 for (Pair<Vertex, String> create : toBeCreated) {
1073 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(), create.getValue1());
1074 if (isDeltaEventsEnabled) {
1075 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1077 this.updatedVertexes.putIfAbsent(v, false);
1078 this.edgeVertexes.add(create.getValue0());
1079 } catch (NoEdgeRuleFoundException ex) {
1080 throw new AAIException(AAI_6129, ex);
1086 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
1087 RelationshipDelta relationshipDelta = new RelationshipDelta(
1089 edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
1090 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
1091 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
1092 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(),
1094 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
1095 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
1099 * Write through defaults.
1102 * @param obj the obj
1103 * @throws AAIUnknownObjectException
1105 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
1106 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
1107 if (latest != null) {
1108 Set<String> required = latest.getRequiredProperties();
1110 for (String field : required) {
1111 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
1112 if (defaultValue != null) {
1113 Object vertexProp = v.property(field).orElse(null);
1114 if (vertexProp == null) {
1115 v.property(field, defaultValue);
1124 * Reflect dependent vertex.
1127 * @param dependentObj the dependent obj
1128 * @return the vertex
1129 * @throws IllegalArgumentException the illegal argument exception
1130 * @throws SecurityException the security exception
1131 * @throws AAIException the AAI exception
1132 * @throws UnsupportedEncodingException the unsupported encoding exception
1133 * @throws AAIUnknownObjectException
1135 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1136 throws AAIException, UnsupportedEncodingException {
1138 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1139 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1140 query.createKeyQuery(dependentObj);
1142 List<Vertex> items = query.toList();
1144 Vertex dependentVertex;
1145 if (items.size() == 1) {
1146 dependentVertex = items.get(0);
1147 this.verifyResourceVersion("update", dependentObj.getDbName(),
1148 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1149 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1151 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1152 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1153 dependentVertex = createNewVertex(dependentObj);
1156 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1161 * Reflect dependent vertex.
1163 * @param parent the parent
1164 * @param child the child
1165 * @param obj the obj
1166 * @return the vertex
1167 * @throws IllegalArgumentException the illegal argument exception
1168 * @throws SecurityException the security exception
1169 * @throws AAIException the AAI exception
1170 * @throws UnsupportedEncodingException the unsupported encoding exception
1171 * @throws AAIUnknownObjectException
1173 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1174 throws AAIException, UnsupportedEncodingException {
1176 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1177 if (parentUri != null) {
1180 addUriIfNeeded(child, parentUri + uri);
1182 processObject(obj, child, requestContext);
1185 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1188 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1189 if (canBeLinked != null && canBeLinked.equals("true")) {
1190 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1191 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1192 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1193 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1195 child.property(AAIProperties.LINKED, true);
1198 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1199 if(isDeltaEventsEnabled) {
1200 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.CREATE);
1207 private SchemaVersion getVerForContext(String requestContext) {
1208 Pattern pattern = Pattern.compile("v[0-9]+");
1209 Matcher m = pattern.matcher(requestContext);
1211 return this.version;
1213 return new SchemaVersion(requestContext);
1220 * @param vertices the vertices
1221 * @param obj the obj
1222 * @param depth the depth
1223 * @param cleanUp the clean up
1224 * @return the introspector
1225 * @throws AAIException the AAI exception
1226 * @throws IllegalArgumentException the illegal argument exception
1227 * @throws SecurityException the security exception
1228 * @throws UnsupportedEncodingException the unsupported encoding exception
1229 * @throws AAIUnknownObjectException
1230 * @throws URISyntaxException
1232 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1233 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1234 final int internalDepth;
1235 if (depth == Integer.MAX_VALUE) {
1236 internalDepth = depth--;
1238 internalDepth = depth;
1240 StopWatch.conditionalStart();
1241 if (vertices.size() > 1 && !obj.isContainer()) {
1242 dbTimeMsecs += StopWatch.stopIfStarted();
1243 throw new AAIException("AAI_6136",
1244 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1245 } else if (obj.isContainer()) {
1247 String listProperty = null;
1248 for (String property : obj.getProperties()) {
1249 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1250 listProperty = property;
1254 final String propertyName = listProperty;
1255 getList = obj.getValue(listProperty);
1258 * This is an experimental multithreading experiment
1261 ExecutorService pool = GetAllPool.getInstance().getPool();
1263 List<Future<Object>> futures = new ArrayList<>();
1265 for (Vertex v : vertices) {
1266 AaiCallable<Object> task = new AaiCallable<Object>() {
1268 public Object process() throws UnsupportedEncodingException, AAIException {
1269 Set<Vertex> seen = new HashSet<>();
1270 Introspector childObject;
1271 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1272 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1273 return childObject.getUnderlyingObject();
1276 futures.add(pool.submit(task));
1279 for (Future<Object> future : futures) {
1281 getList.add(future.get());
1282 } catch (InterruptedException e) {
1283 dbTimeMsecs += StopWatch.stopIfStarted();
1284 Thread.currentThread().interrupt();
1285 throw new AAIException("AAI_4000", e);
1286 } catch (ExecutionException e) {
1287 dbTimeMsecs += StopWatch.stopIfStarted();
1288 throw new AAIException("AAI_4000", e);
1291 } else if (vertices.size() == 1) {
1292 Set<Vertex> seen = new HashSet<>();
1293 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1298 dbTimeMsecs += StopWatch.stopIfStarted();
1305 * @param vertices the vertices
1306 * @param obj the obj
1307 * @param depth the depth
1308 * @param cleanUp the clean up
1309 * @return the introspector
1310 * @throws AAIException the AAI exception
1311 * @throws IllegalArgumentException the illegal argument exception
1312 * @throws SecurityException the security exception
1313 * @throws UnsupportedEncodingException the unsupported encoding exception
1314 * @throws AAIUnknownObjectException
1315 * @throws URISyntaxException
1317 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1318 String cleanUp) throws UnsupportedEncodingException, AAIException {
1319 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1325 * @param obj the obj
1327 * @param seen the seen
1328 * @param depth the depth
1329 * @param cleanUp the clean up
1330 * @return the introspector
1331 * @throws IllegalArgumentException the illegal argument exception
1332 * @throws SecurityException the security exception
1333 * @throws UnsupportedEncodingException the unsupported encoding exception
1334 * @throws AAIException the AAI exception
1335 * @throws AAIUnknownObjectException
1336 * @throws URISyntaxException
1338 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1339 String cleanUp) throws AAIException, UnsupportedEncodingException {
1340 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1346 * @param obj the obj
1348 * @param seen the seen
1349 * @param depth the depth
1350 * @param cleanUp the clean up
1351 * @return the introspector
1352 * @throws IllegalArgumentException the illegal argument exception
1353 * @throws SecurityException the security exception
1354 * @throws UnsupportedEncodingException the unsupported encoding exception
1355 * @throws AAIException the AAI exception
1356 * @throws AAIUnknownObjectException
1357 * @throws URISyntaxException
1359 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1360 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1368 boolean modified = false;
1369 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1370 List<Object> getList = null;
1372 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1373 this.copySimpleProperty(property, obj, v);
1376 if (obj.isComplexType(property)) {
1377 /* container case */
1379 if (!property.equals("relationship-list") && depth >= 0) {
1380 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1381 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1382 if (result != null) {
1383 obj.setValue(property, argumentObject.getUnderlyingObject());
1386 } else if (property.equals("relationship-list") && !nodeOnly) {
1387 /* relationships need to be handled correctly */
1388 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1389 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1390 if (relationshipList != null) {
1391 obj.setValue(property, relationshipList.getUnderlyingObject());
1396 } else if (obj.isListType(property)) {
1398 if (property.equals("any")) {
1401 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1402 if (obj.isComplexGenericType(property) && depth >= 0) {
1403 final String childDbName = convertFromCamelCase(genericType);
1404 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1408 rule = edgeRules.getRule(
1409 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1410 } catch (EdgeRuleNotFoundException e) {
1411 throw new NoEdgeRuleFoundException(e);
1412 } catch (AmbiguousRuleChoiceException e) {
1413 throw new MultipleEdgeRuleFoundException(e);
1415 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1417 Direction ruleDirection = rule.getDirection();
1418 List<Vertex> verticesList = new ArrayList<>();
1419 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1420 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1421 verticesList.add(vertex);
1424 if (!verticesList.isEmpty()) {
1425 getList = obj.getValue(property);
1428 for (Vertex childVertex : verticesList) {
1429 if (!seen.contains(childVertex)) {
1430 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1433 dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1434 if (result != null && getList != null) {
1435 getList.add(argumentObject.getUnderlyingObject());
1440 LOGGER.warn("Cycle found while serializing vertex id={}",
1441 childVertex.id().toString());
1444 if (processed == 0) {
1445 // vertices were all seen, reset the list
1448 if (processed > 0) {
1452 } else if (obj.isSimpleGenericType(property)) {
1453 List<Object> temp = this.engine.getListProperty(v, property);
1455 getList = (List<Object>) obj.getValue(property);
1456 getList.addAll(temp);
1464 // no changes were made to this obj, discard the instance
1468 this.enrichData(obj, v);
1473 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1474 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1475 if (nodeType == null) {
1476 throw new AAIException("AAI_6143");
1479 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1480 Set<Vertex> seen = new HashSet<>();
1482 StopWatch.conditionalStart();
1483 this.dbToObject(obj, v, seen, depth, true, FALSE);
1484 dbTimeMsecs += StopWatch.stopIfStarted();
1489 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1490 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1493 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1494 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1495 if (nodeType == null) {
1496 throw new AAIException("AAI_6143");
1498 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1499 Set<Vertex> seen = new HashSet<>();
1500 StopWatch.conditionalStart();
1501 this.dbToObject(obj, v, seen, depth, false, FALSE);
1502 dbTimeMsecs += StopWatch.stopIfStarted();
1507 * Copy simple property.
1509 * @param property the property
1510 * @param obj the obj
1512 * @throws IllegalArgumentException the illegal argument exception
1513 * @throws SecurityException the security exception
1515 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1516 final Object temp = getProperty(obj, property, v);
1518 obj.setValue(property, temp);
1522 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1524 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1525 String[] simplePropsArray = new String[simpleProperties.size()];
1526 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1528 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1530 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1532 return simplePropsHashMap;
1535 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1536 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1537 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1538 return relationshipList;
1542 * Creates the relationship list.
1545 * @param obj the obj
1546 * @param cleanUp the clean up
1547 * @return the object
1548 * @throws IllegalArgumentException the illegal argument exception
1549 * @throws SecurityException the security exception
1550 * @throws UnsupportedEncodingException the unsupported encoding exception
1551 * @throws AAIException the AAI exception
1552 * @throws URISyntaxException
1554 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1555 throws UnsupportedEncodingException, AAIException {
1556 // default boolean value for isSkipRelatedTo is false
1557 return createRelationshipList(v, obj, cleanUp, false);
1561 * Creates the relationship list.
1564 * @param obj the obj
1565 * @param cleanUp the clean up
1566 * @param isSkipRelatedTo to determine adding related-to-property in response
1567 * @return the object
1568 * @throws IllegalArgumentException the illegal argument exception
1569 * @throws SecurityException the security exception
1570 * @throws UnsupportedEncodingException the unsupported encoding exception
1571 * @throws AAIException the AAI exception
1572 * @throws URISyntaxException
1574 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1575 throws UnsupportedEncodingException, AAIException {
1577 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1578 VertexProperty nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1580 if (!nodeTypeProperty.isPresent()) {
1581 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1585 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1587 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1589 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1591 EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType)
1592 .edgeType(EdgeType.COUSIN)
1593 .version(obj.getVersion());
1595 for (Path path : paths) {
1596 if (path.size() < 3) {
1601 // v ----related-to--> otherV
1602 // In the above case,
1603 // path objects get(0) returns vertex v
1604 // path objects.get(1) returns edge related-to
1605 // path objects.get(2) returns vertex otherV
1606 Edge edge = path.get(1);
1607 Vertex otherV = path.get(2);
1609 // TODO: Come back and revisit this code
1610 // Create a query based on the a nodetype and b nodetype
1611 // which is also a cousin edge and ensure the version
1612 // is used properly so for example in order to be backwards
1613 // compatible if we had allowed a edge between a and b
1614 // in a previous release and we decided to remove it from
1615 // the edge rules in the future we can display the edge
1616 // only for the older apis and the new apis if the edge rule
1617 // is removed will not be seen in the newer version of the API
1620 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1621 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1626 String edgeLabel = edge.label();
1627 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1629 if (!edgeIngestor.hasRule(ruleQuery)) {
1630 LOGGER.debug( "Caught an edge rule not found for query {}", ruleQuery);
1634 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1635 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1636 if (result != null) {
1637 relationshipObjList.add(result);
1642 if (relationshipObjList.isEmpty()) {
1650 * Process edge relationship.
1652 * @param relationshipObj the relationship obj
1653 * @param edgeLabel the edge's label
1654 * @param cleanUp the clean up
1655 * @return the object
1656 * @throws IllegalArgumentException the illegal argument exception
1657 * @throws SecurityException the security exception
1658 * @throws UnsupportedEncodingException the unsupported encoding exception
1659 * @throws AAIUnknownObjectException
1660 * @throws URISyntaxException
1662 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1663 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1665 VertexProperty aaiUriProperty = cousin.property("aai-uri");
1667 if (!aaiUriProperty.isPresent()) {
1671 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1673 URIToRelationshipObject uriParser;
1674 Introspector result;
1676 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1677 result = uriParser.getResult();
1678 } catch (AAIException | URISyntaxException e) {
1679 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1680 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1684 VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1686 if (cousinVertexNodeType.isPresent()) {
1687 String cousinType = cousinVertexNodeType.value().toString();
1688 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1689 this.addRelatedToProperty(result, cousin, cousinType);
1693 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1694 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1697 return result.getUnderlyingObject();
1701 * Gets the URI for vertex.
1704 * @return the URI for vertex
1705 * @throws IllegalArgumentException the illegal argument exception
1706 * @throws SecurityException the security exception
1707 * @throws UnsupportedEncodingException the unsupported encoding exception
1708 * @throws AAIUnknownObjectException
1710 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1712 return getURIForVertex(v, false);
1715 public URI getURIForVertex(Vertex v, boolean overwrite) {
1716 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1718 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1720 if (aaiUri != null && !overwrite) {
1721 uri = UriBuilder.fromPath(aaiUri).build();
1728 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1729 throws AAIUnknownObjectException {
1734 obj = this.loader.introspectorFromName(cousinType);
1735 } catch (AAIUnknownObjectException ex) {
1736 if (LOGGER.isTraceEnabled()) {
1737 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1738 cousinType, cousinVertex.id());
1743 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1744 List<Introspector> relatedToProperties = new ArrayList<>();
1746 if (nameProps != null) {
1747 String[] props = nameProps.split(",");
1748 for (String prop : props) {
1749 final Object temp = getProperty(obj, prop, cousinVertex);
1750 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1751 relatedTo.setValue("property-key", cousinType + "." + prop);
1752 relatedTo.setValue("property-value", temp);
1753 relatedToProperties.add(relatedTo);
1757 if (!relatedToProperties.isEmpty()) {
1758 List<Object> relatedToList = relationship.getValue("related-to-property");
1759 for (Introspector introspector : relatedToProperties) {
1760 relatedToList.add(introspector.getUnderlyingObject());
1766 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1768 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1769 String dbPropertyName = prop;
1771 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1772 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1775 return vertex.<Object>property(dbPropertyName).orElse(null);
1781 * @param relationship the relationship
1782 * @param inputVertex the input vertex
1783 * @return true, if successful
1784 * @throws UnsupportedEncodingException the unsupported encoding exception
1785 * @throws AAIException the AAI exception
1787 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1788 throws UnsupportedEncodingException, AAIException {
1790 Vertex relatedVertex;
1791 StopWatch.conditionalStart();
1792 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1794 String label = null;
1795 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1796 label = relationship.getValue(RELATIONSHIP_LABEL);
1799 List<Vertex> results = parser.getQueryBuilder().toList();
1800 if (results.isEmpty()) {
1801 dbTimeMsecs += StopWatch.stopIfStarted();
1802 AAIException e = new AAIException(AAI_6129,
1803 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1804 e.getTemplateVars().add(parser.getResultType());
1805 e.getTemplateVars().add(parser.getUri().toString());
1808 // still an issue if there's more than one
1809 relatedVertex = results.get(0);
1812 if (relatedVertex != null) {
1816 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1818 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1819 if(isDeltaEventsEnabled) {
1820 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1823 // attempted to link two vertexes already linked
1826 dbTimeMsecs += StopWatch.stopIfStarted();
1830 dbTimeMsecs += StopWatch.stopIfStarted();
1831 return relatedVertex;
1835 * Gets all the edges between of the type with the specified label.
1837 * @param aVertex the out vertex
1838 * @param bVertex the in vertex
1839 * @return the edges between
1841 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1845 if (bVertex != null) {
1846 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1847 if (EdgeType.TREE.equals(type)) {
1848 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1849 if (edgeRule.getDirection().equals(Direction.IN)) {
1850 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1851 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1852 .not(__.has(EdgeField.PRIVATE.toString(), true));
1854 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1855 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1856 .not(__.has(EdgeField.PRIVATE.toString(), true));
1858 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1860 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1861 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1862 .not(__.has(EdgeField.PRIVATE.toString(), true));
1863 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1865 List<Edge> list = findEdgesBetween.toList();
1866 if (!list.isEmpty()) {
1867 result = list.get(0);
1875 * Gets all the edges string between of the type.
1877 * @param aVertex the out vertex
1878 * @param bVertex the in vertex
1879 * @return the edges between
1880 * @throws NoEdgeRuleFoundException
1882 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1884 List<String> result = new ArrayList<>();
1886 if (bVertex != null) {
1887 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1888 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1889 if (EdgeType.TREE.equals(type)) {
1890 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1891 __.has(EdgeField.PRIVATE.toString(), true)));
1893 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1894 .not(__.has(EdgeField.PRIVATE.toString(), true));
1896 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1897 result = findEdgesBetween.label().toList();
1903 * Gets all the edges between the vertexes with the label and type.
1905 * @param vertexOut the out vertex
1906 * @param vertexIn the in vertex
1908 * @return the edges between
1909 * @throws AAIException the AAI exception
1911 private Edge getEdgesBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1915 if (vertexIn != null) {
1916 String aType = vertexOut.<String>property(AAIProperties.NODE_TYPE).value();
1917 String bType = vertexIn.<String>property(AAIProperties.NODE_TYPE).value();
1918 EdgeRuleQuery query = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1921 rule = edgeRules.getRule(query);
1922 } catch (EdgeRuleNotFoundException e) {
1923 throw new NoEdgeRuleFoundException(e);
1924 } catch (AmbiguousRuleChoiceException e) {
1925 throw new MultipleEdgeRuleFoundException(e);
1927 edge = this.getEdgeBetweenWithLabel(type, vertexOut, vertexIn, rule);
1934 * Gets the edge between with the label and edge type.
1936 * @param vertexOut the out vertex
1937 * @param vertexIn the in vertex
1939 * @return the edge between
1940 * @throws AAIException the AAI exception
1941 * @throws NoEdgeRuleFoundException
1943 public Edge getEdgeBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1945 StopWatch.conditionalStart();
1946 if (vertexIn != null) {
1948 Edge edge = this.getEdgesBetween(type, vertexOut, vertexIn, label);
1950 dbTimeMsecs += StopWatch.stopIfStarted();
1955 dbTimeMsecs += StopWatch.stopIfStarted();
1959 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1960 return this.getEdgeBetween(type, aVertex, bVertex, null);
1966 * @param relationship the relationship
1967 * @param inputVertex the input vertex
1968 * @return true, if successful
1969 * @throws UnsupportedEncodingException the unsupported encoding exception
1970 * @throws AAIException the AAI exception
1972 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1973 throws UnsupportedEncodingException, AAIException {
1975 Vertex relatedVertex;
1976 StopWatch.conditionalStart();
1977 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1979 List<Vertex> results = parser.getQueryBuilder().toList();
1981 String label = null;
1982 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1983 label = relationship.getValue(RELATIONSHIP_LABEL);
1986 if (results.isEmpty()) {
1987 dbTimeMsecs += StopWatch.stopIfStarted();
1988 return Optional.empty();
1991 relatedVertex = results.get(0);
1994 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1995 } catch (NoEdgeRuleFoundException e) {
1996 dbTimeMsecs += StopWatch.stopIfStarted();
1997 throw new AAIException(AAI_6129, e);
2000 if (isDeltaEventsEnabled) {
2001 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
2002 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
2005 dbTimeMsecs += StopWatch.stopIfStarted();
2006 return Optional.of(relatedVertex);
2008 dbTimeMsecs += StopWatch.stopIfStarted();
2009 return Optional.empty();
2015 * Delete with traversal.
2017 * @param startVertex the start vertex
2019 public void deleteWithTraversal(Vertex startVertex) {
2020 StopWatch.conditionalStart();
2021 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
2022 this.delete(results);
2026 * Removes the list of vertexes from the graph
2028 * Current the vertex label will just be vertex but
2029 * in the future the aai-node-type property will be replaced
2030 * by using the vertex label as when retrieving an vertex
2031 * and retrieving an single property on an vertex will pre-fetch
2032 * all the properties of that vertex and this is due to the following property
2035 * query.fast-property=true
2038 * JanusGraph doesn't provide the capability to override that
2039 * at a transaction level and there is a plan to move to vertex label
2040 * so it is best to utilize this for now and when the change is applied
2042 * @param vertices - list of vertices to delete from the graph
2044 void delete(List<Vertex> vertices) {
2045 StopWatch.conditionalStart();
2047 for (Vertex v : vertices) {
2048 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
2049 if(isDeltaEventsEnabled) {
2050 deltaForVertexDelete(v);
2052 //add the cousin vertexes of v to have their resource-version updated and notified on.
2053 v.edges(Direction.BOTH)
2054 .forEachRemaining(e -> {
2055 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
2056 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
2057 e.bothVertices().forEachRemaining(cousinV -> {
2058 if (!v.equals(cousinV)) {
2059 edgeVertexes.add(cousinV);
2065 //if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
2066 // it should be removed from them as v is to be deleted
2067 edgeVertexes.remove(v);
2068 updatedVertexes.remove(v);
2072 dbTimeMsecs += StopWatch.stopIfStarted();
2075 private void deltaForVertexDelete(Vertex vertex) {
2076 String aaiUri = vertex.property(AAIProperties.AAI_URI).value().toString();
2077 vertex.keys().forEach(k -> {
2078 List<Object> list = new ArrayList<>();
2079 vertex.properties(k).forEachRemaining(vp -> list.add(vp.value()));
2080 if (list.size() == 1) {
2081 addPropDelta(aaiUri, k,
2082 PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)),
2083 DeltaAction.DELETE);
2085 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list),
2086 DeltaAction.DELETE);
2090 vertex.edges(Direction.BOTH).forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
2097 * @param resourceVersion the resource version
2098 * @throws IllegalArgumentException the illegal argument exception
2099 * @throws AAIException the AAI exception
2100 * @throws InterruptedException the interrupted exception
2102 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
2103 throws IllegalArgumentException, AAIException {
2105 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2107 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
2108 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
2109 * So its better to make these in 2 steps
2111 if (result && !deletableVertices.isEmpty()) {
2112 result = verifyPreventDeleteSemantics(deletableVertices);
2117 deleteWithTraversal(v);
2118 } catch (IllegalStateException e) {
2119 throw new AAIException("AAI_6110", e);
2128 * @param vertex the vertex
2129 * @param resourceVersion the resource version
2130 * @throws IllegalArgumentException the illegal argument exception
2131 * @throws AAIException the AAI exception
2132 * @throws InterruptedException the interrupted exception
2134 public void delete(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2135 throws IllegalArgumentException, AAIException {
2137 boolean result = verifyDeleteSemantics(vertex, resourceVersion, enableResourceVersion);
2142 deleteWithTraversal(vertex);
2143 } catch (IllegalStateException e) {
2144 throw new AAIException("AAI_6110", e);
2151 * Verify delete semantics.
2153 * @param vertex the vertex
2154 * @param resourceVersion the resource version
2155 * @return true, if successful
2156 * @throws AAIException the AAI exception
2158 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2159 throws AAIException {
2162 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2163 if (enableResourceVersion) {
2164 this.verifyResourceVersion("delete", nodeType,
2165 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2167 List<Vertex> vertices = new ArrayList<>();
2168 vertices.add(vertex);
2169 result = verifyPreventDeleteSemantics(vertices);
2175 * Verify Prevent delete semantics.
2177 * @param vertices the list of vertices
2178 * @return true, if successful
2179 * @throws AAIException the AAI exception
2181 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2182 boolean result = true;
2183 String errorDetail = " unknown delete semantic found";
2184 String aaiExceptionCode = "";
2186 StopWatch.conditionalStart();
2188 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2189 * "prevent-delete" condition
2190 * If yes - that should prevent the deletion of the vertex
2191 * Dedup makes sure we dont capture the prevent-delete vertices twice
2192 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2195 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2196 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2197 .values(AAIProperties.NODE_TYPE),
2198 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2199 .values(AAIProperties.NODE_TYPE))
2202 dbTimeMsecs += StopWatch.stopIfStarted();
2203 if (!preventDeleteVertices.isEmpty()) {
2204 aaiExceptionCode = "AAI_6110";
2205 errorDetail = String.format(
2206 "Object is being reference by additional objects preventing it from being deleted." +
2207 " Please clean up references from the following types %s",
2208 preventDeleteVertices);
2212 throw new AAIException(aaiExceptionCode, errorDetail);
2218 * Verify resource version.
2220 * @param action the action
2221 * @param nodeType the node type
2222 * @param currentResourceVersion the current resource version
2223 * @param resourceVersion the resource version
2224 * @param uri the uri
2225 * @return true, if successful
2226 * @throws AAIException the AAI exception
2228 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2229 String resourceVersion, String uri) throws AAIException {
2230 String enabled = "";
2231 String errorDetail = "";
2232 String aaiExceptionCode = "";
2233 boolean isDeleteResourceVersionOk = true;
2234 if (currentResourceVersion == null) {
2235 currentResourceVersion = "";
2238 if (resourceVersion == null) {
2239 resourceVersion = "";
2242 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2244 } catch (AAIException e) {
2245 ErrorLogHelper.logException(e);
2247 if (enabled.equals("true")) {
2248 if ("delete".equals(action)) {
2249 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2251 if ((!isDeleteResourceVersionOk)
2252 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2253 if ("create".equals(action) && !resourceVersion.equals("")) {
2254 errorDetail = "resource-version passed for " + action + " of " + uri;
2255 aaiExceptionCode = "AAI_6135";
2256 } else if (resourceVersion.equals("")) {
2257 errorDetail = "resource-version not passed for " + action + " of " + uri;
2258 aaiExceptionCode = "AAI_6130";
2260 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2261 aaiExceptionCode = "AAI_6131";
2264 throw new AAIException(aaiExceptionCode, errorDetail);
2272 * Verify resource version for delete.
2274 * @param currentResourceVersion the current resource version
2275 * @param resourceVersion the resource version
2276 * @return true, if successful or false if there is a mismatch
2278 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2280 boolean isDeleteResourceVersionOk = true;
2281 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2282 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2284 if ((!currentResourceVersion.equals(resourceVersion))
2285 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2286 isDeleteResourceVersionOk = false;
2288 return isDeleteResourceVersionOk;
2292 * Convert from camel case.
2294 * @param name the name
2295 * @return the string
2297 private String convertFromCamelCase(String name) {
2299 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2301 NamingExceptions exceptions = NamingExceptions.getInstance();
2302 result = exceptions.getDBName(result);
2307 private boolean canModify(Introspector obj, String propName, String requestContext) {
2308 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2309 if (readOnly != null) {
2310 final String[] items = readOnly.split(",");
2311 for (String item : items) {
2312 if (requestContext.equals(item)) {
2320 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2322 SideEffectRunner.Builder runnerBuilder =
2323 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataCopy.class)
2324 .addSideEffect(PrivateEdge.class);
2325 if (isMultiTenancyEnabled) {
2326 runnerBuilder.addSideEffect(OwnerCheck.class);
2328 runnerBuilder.build().execute(obj, self);
2331 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2333 SideEffectRunner runner =
2334 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2336 runner.execute(obj, self);
2339 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2341 SideEffectRunner.Builder runnerBuilder =
2342 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class);
2344 if (isMultiTenancyEnabled) {
2345 runnerBuilder.addSideEffect(OwnerCheck.class);
2347 runnerBuilder.build().execute(obj, self);
2350 public double getDBTimeMsecs() {
2351 return (dbTimeMsecs);
2355 * Db to object With Filters
2356 * This is for a one-time run with Tenant Isloation to only filter relationships
2358 * @param obj the obj
2359 * @param vertex the vertex from the graph
2360 * @param depth the depth
2361 * @param nodeOnly specify if to exclude relationships or not
2362 * @param filterCousinNodes
2363 * @return the introspector
2364 * @throws AAIException the AAI exception
2365 * @throws IllegalAccessException the illegal access exception
2366 * @throws IllegalArgumentException the illegal argument exception
2367 * @throws InvocationTargetException the invocation target exception
2368 * @throws SecurityException the security exception
2369 * @throws InstantiationException the instantiation exception
2370 * @throws NoSuchMethodException the no such method exception
2371 * @throws UnsupportedEncodingException the unsupported encoding exception
2372 * @throws MalformedURLException the malformed URL exception
2373 * @throws AAIUnknownObjectException
2374 * @throws URISyntaxException
2376 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertex, Set<Vertex> seen, int depth, boolean nodeOnly,
2377 List<String> filterCousinNodes, List<String> filterParentNodes)
2378 throws AAIException, UnsupportedEncodingException {
2379 return dbToObjectWithFilters(obj, vertex, seen, depth, nodeOnly,
2380 filterCousinNodes, filterParentNodes, false);
2384 * Db to object With Filters
2385 * This is for a one-time run with Tenant Isloation to only filter relationships
2386 * TODO: Chnage the original dbToObject to take filter parent/cousins
2388 * @param obj the obj
2389 * @param vertexParam the vertex from the graph
2390 * @param depth the depth
2391 * @param nodeOnly specify if to exclude relationships or not
2392 * @param filterCousinNodes
2393 * @param isSkipRelatedTo determine to incorporated related-to-property data
2394 * @return the introspector
2395 * @throws AAIException the AAI exception
2396 * @throws IllegalAccessException the illegal access exception
2397 * @throws IllegalArgumentException the illegal argument exception
2398 * @throws InvocationTargetException the invocation target exception
2399 * @throws SecurityException the security exception
2400 * @throws InstantiationException the instantiation exception
2401 * @throws NoSuchMethodException the no such method exception
2402 * @throws UnsupportedEncodingException the unsupported encoding exception
2403 * @throws MalformedURLException the malformed URL exception
2404 * @throws AAIUnknownObjectException
2405 * @throws URISyntaxException
2407 // TODO - See if you can merge the 2 dbToObjectWithFilters
2408 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertexParam, Set<Vertex> seen, int depth, boolean nodeOnly,
2409 List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2410 throws AAIException, UnsupportedEncodingException {
2411 String cleanUp = FALSE;
2416 seen.add(vertexParam);
2417 boolean modified = false;
2418 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2419 List<Object> getList = null;
2421 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2422 this.copySimpleProperty(property, obj, vertexParam);
2425 if (obj.isComplexType(property)) {
2426 /* container case */
2428 if (!property.equals("relationship-list") && depth >= 0) {
2429 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2430 Object result = dbToObjectWithFilters(argumentObject, vertexParam, seen, depth + 1, nodeOnly,
2431 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2432 if (result != null) {
2433 obj.setValue(property, argumentObject.getUnderlyingObject());
2436 } else if (property.equals("relationship-list") && !nodeOnly) {
2437 /* relationships need to be handled correctly */
2438 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2440 createFilteredRelationshipList(vertexParam, relationshipList, cleanUp,
2441 filterCousinNodes, isSkipRelatedTo);
2442 if (relationshipList != null) {
2443 obj.setValue(property, relationshipList.getUnderlyingObject());
2448 } else if (obj.isListType(property)) {
2450 if (property.equals("any")) {
2453 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2454 if (obj.isComplexGenericType(property) && depth >= 0) {
2455 final String childDbName = convertFromCamelCase(genericType);
2456 String vertexType = vertexParam.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2459 boolean isThisParentRequired =
2460 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2462 EdgeRuleQuery query = new EdgeRuleQuery.Builder(vertexType, childDbName).edgeType(EdgeType.TREE).build();
2465 rule = edgeRules.getRule(query);
2466 } catch (EdgeRuleNotFoundException e) {
2467 throw new NoEdgeRuleFoundException(e);
2468 } catch (AmbiguousRuleChoiceException e) {
2469 throw new MultipleEdgeRuleFoundException(e);
2471 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2472 Direction ruleDirection = rule.getDirection();
2473 List<Vertex> verticesList = new ArrayList<>();
2474 vertexParam.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2475 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2476 verticesList.add(vertex);
2479 if (!verticesList.isEmpty()) {
2480 getList = obj.getValue(property);
2483 for (Vertex childVertex : verticesList) {
2484 if (!seen.contains(childVertex)) {
2485 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2487 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2488 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2489 if (result != null && getList != null) {
2490 getList.add(argumentObject.getUnderlyingObject());
2495 LOGGER.warn("Cycle found while serializing vertex id={}",
2496 childVertex.id().toString());
2499 if (processed == 0) {
2500 // vertices were all seen, reset the list
2503 if (processed > 0) {
2507 } else if (obj.isSimpleGenericType(property)) {
2508 List<Object> temp = this.engine.getListProperty(vertexParam, property);
2510 getList = obj.getValue(property);
2511 getList.addAll(temp);
2522 // no changes were made to this obj, discard the instance
2526 this.enrichData(obj, vertexParam);
2532 * Creates the relationship list with the filtered node types.
2534 * @param vertex the vertex
2535 * @param obj the obj
2536 * @param cleanUp the clean up
2537 * @return the object
2538 * @throws IllegalArgumentException the illegal argument exception
2539 * @throws SecurityException the security exception
2540 * @throws UnsupportedEncodingException the unsupported encoding exception
2541 * @throws AAIException the AAI exception
2543 private Introspector createFilteredRelationshipList(Vertex vertex, Introspector obj, String cleanUp,
2544 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2545 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(vertex);
2547 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2548 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2549 return filterNodes.parallelStream().anyMatch(node::contains);
2552 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2554 List<Vertex> cousins = new ArrayList<>();
2555 cousinVertices.forEachRemaining(cousins::add);
2556 for (Vertex cousin : cousins) {
2558 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2559 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2560 if (result != null) {
2561 relationshipObjList.add(result);
2566 if (relationshipObjList.isEmpty()) {
2573 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2574 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2575 return this.edgeVertexes;
2578 public void addVertexToEdgeVertexes(Vertex vertex) {
2579 this.edgeVertexes.add(vertex);
2582 private String urlToUri(String url) {
2583 if (url.startsWith("/")) {
2584 url = url.substring(1);
2587 if (url.endsWith("/")) {
2588 url = url.substring(0, url.length() - 1);
2591 // TODO - Check if this makes to do for model driven for base uri path
2592 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2593 if (url.charAt(0) != '/') {