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=========================================================
21 package org.onap.aai.serialization.db;
23 import com.google.common.base.CaseFormat;
25 import java.io.UnsupportedEncodingException;
26 import java.lang.reflect.Array;
27 import java.lang.reflect.InvocationTargetException;
28 import java.net.MalformedURLException;
30 import java.net.URISyntaxException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedHashMap;
38 import java.util.LinkedHashSet;
39 import java.util.List;
41 import java.util.Optional;
43 import java.util.UUID;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.ExecutorService;
46 import java.util.concurrent.Future;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
50 import javax.ws.rs.core.UriBuilder;
52 import org.apache.commons.lang3.StringUtils;
53 import org.apache.tinkerpop.gremlin.process.traversal.Path;
54 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
55 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
56 import org.apache.tinkerpop.gremlin.structure.Direction;
57 import org.apache.tinkerpop.gremlin.structure.Edge;
58 import org.apache.tinkerpop.gremlin.structure.Vertex;
59 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
60 import org.janusgraph.core.SchemaViolationException;
61 import org.javatuples.Pair;
62 import org.onap.aai.concurrent.AaiCallable;
63 import org.onap.aai.config.SpringContextAware;
64 import org.onap.aai.db.props.AAIProperties;
65 import org.onap.aai.edges.EdgeIngestor;
66 import org.onap.aai.edges.EdgeRule;
67 import org.onap.aai.edges.EdgeRuleQuery;
68 import org.onap.aai.edges.enums.AAIDirection;
69 import org.onap.aai.edges.enums.EdgeField;
70 import org.onap.aai.edges.enums.EdgeProperty;
71 import org.onap.aai.edges.enums.EdgeType;
72 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
73 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
74 import org.onap.aai.exceptions.AAIException;
75 import org.onap.aai.introspection.Introspector;
76 import org.onap.aai.introspection.IntrospectorFactory;
77 import org.onap.aai.introspection.Loader;
78 import org.onap.aai.introspection.LoaderFactory;
79 import org.onap.aai.introspection.ModelType;
80 import org.onap.aai.introspection.PropertyPredicates;
81 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
82 import org.onap.aai.introspection.sideeffect.DataCopy;
83 import org.onap.aai.introspection.sideeffect.DataLinkReader;
84 import org.onap.aai.introspection.sideeffect.DataLinkWriter;
85 import org.onap.aai.introspection.sideeffect.OwnerCheck;
86 import org.onap.aai.introspection.sideeffect.PrivateEdge;
87 import org.onap.aai.introspection.sideeffect.SideEffectRunner;
88 import org.onap.aai.logging.ErrorLogHelper;
89 import org.onap.aai.logging.LogFormatTools;
90 import org.onap.aai.logging.StopWatch;
91 import org.onap.aai.parsers.query.QueryParser;
92 import org.onap.aai.parsers.relationship.RelationshipToURI;
93 import org.onap.aai.parsers.uri.URIParser;
94 import org.onap.aai.parsers.uri.URIToObject;
95 import org.onap.aai.parsers.uri.URIToRelationshipObject;
96 import org.onap.aai.query.builder.QueryBuilder;
97 import org.onap.aai.schema.enums.ObjectMetadata;
98 import org.onap.aai.schema.enums.PropertyMetadata;
99 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
100 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
101 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
102 import org.onap.aai.serialization.engines.query.QueryEngine;
103 import org.onap.aai.setup.SchemaVersion;
104 import org.onap.aai.setup.SchemaVersions;
105 import org.onap.aai.util.AAIConfig;
106 import org.onap.aai.util.AAIConstants;
107 import org.onap.aai.util.delta.DeltaAction;
108 import org.onap.aai.util.delta.ObjectDelta;
109 import org.onap.aai.util.delta.PropertyDelta;
110 import org.onap.aai.util.delta.PropertyDeltaFactory;
111 import org.onap.aai.util.delta.RelationshipDelta;
112 import org.onap.aai.workarounds.NamingExceptions;
113 import org.slf4j.Logger;
114 import org.slf4j.LoggerFactory;
115 import org.springframework.context.ApplicationContext;
117 public class DBSerializer {
119 private static final Logger LOGGER = LoggerFactory.getLogger(DBSerializer.class);
120 private static final String RELATIONSHIP_LABEL = "relationship-label";
121 private static final String RELATIONSHIP = "relationship";
122 public static final String FALSE = "false";
123 public static final String AAI_6145 = "AAI_6145";
124 public static final String AAI_6129 = "AAI_6129";
126 private final TransactionalGraphEngine engine;
127 private final String sourceOfTruth;
128 private final Set<String> groups;
129 private final ModelType introspectionType;
130 private final SchemaVersion version;
131 private final Loader latestLoader;
132 private EdgeSerializer edgeSer;
133 private EdgeIngestor edgeRules;
134 private final Loader loader;
135 private final String baseURL;
136 private double dbTimeMsecs = 0;
137 private long currentTimeMillis;
139 private SchemaVersions schemaVersions;
140 private Set<String> namedPropNodes;
141 private Map<String, ObjectDelta> objectDeltas = new LinkedHashMap<>();
142 private Map<Vertex, Boolean> updatedVertexes = new LinkedHashMap<>();
143 private Set<Vertex> edgeVertexes = new LinkedHashSet<>();
144 private Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> impliedDeleteUriObjectPair =
145 new LinkedHashMap<>();
146 private int notificationDepth;
147 private boolean isDeltaEventsEnabled;
148 private boolean isMultiTenancyEnabled;
151 * Instantiates a new DB serializer.
153 * @param version the version
154 * @param engine the engine
155 * @param introspectionType the introspection type
156 * @param sourceOfTruth the source of truth
157 * @throws AAIException
159 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
160 String sourceOfTruth) throws AAIException {
161 this.engine = engine;
162 this.sourceOfTruth = sourceOfTruth;
163 this.groups = Collections.EMPTY_SET;
164 this.introspectionType = introspectionType;
165 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
166 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
167 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
169 this.version = version;
171 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
172 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
173 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
174 this.currentTimeMillis = System.currentTimeMillis();
175 // If creating the DBSerializer the old way then set the notification depth to maximum
176 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
180 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
181 String sourceOfTruth, Set<String> groups) throws AAIException {
182 this.engine = engine;
183 this.sourceOfTruth = sourceOfTruth;
184 this.groups = groups;
185 this.introspectionType = introspectionType;
186 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
187 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
188 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
190 this.version = version;
192 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
193 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
194 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
195 this.currentTimeMillis = System.currentTimeMillis();
196 // If creating the DBSerializer the old way then set the notification depth to maximum
197 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
201 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
202 String sourceOfTruth, int notificationDepth) throws AAIException {
203 this.engine = engine;
204 this.sourceOfTruth = sourceOfTruth;
205 this.groups = Collections.EMPTY_SET;
206 this.introspectionType = introspectionType;
207 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
208 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
209 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
211 this.version = version;
213 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
214 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
215 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
216 this.currentTimeMillis = System.currentTimeMillis();
217 this.notificationDepth = notificationDepth;
221 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
222 String sourceOfTruth, Set<String> groups, int notificationDepth) throws AAIException {
223 this.engine = engine;
224 this.sourceOfTruth = sourceOfTruth;
225 this.groups = groups;
226 this.introspectionType = introspectionType;
227 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
228 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
229 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
231 this.version = version;
233 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
234 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
235 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
236 this.currentTimeMillis = System.currentTimeMillis();
237 this.notificationDepth = notificationDepth;
241 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
242 String sourceOfTruth, int notificationDepth, String serverBase) throws AAIException {
243 this.engine = engine;
244 this.sourceOfTruth = sourceOfTruth;
245 this.groups = Collections.EMPTY_SET;
246 this.introspectionType = introspectionType;
247 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
248 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
249 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
251 this.version = version;
253 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
254 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
255 this.baseURL = serverBase;
256 this.currentTimeMillis = System.currentTimeMillis();
257 this.notificationDepth = notificationDepth;
261 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType,
262 String sourceOfTruth, Set<String> groups, int notificationDepth, String serverBase) throws AAIException {
263 this.engine = engine;
264 this.sourceOfTruth = sourceOfTruth;
265 this.groups = groups;
266 this.introspectionType = introspectionType;
267 this.schemaVersions = (SchemaVersions) SpringContextAware.getBean("schemaVersions");
268 SchemaVersion latestVersion = schemaVersions.getDefaultVersion();
269 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType,
271 this.version = version;
273 SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
274 this.namedPropNodes = this.latestLoader.getNamedPropNodes();
275 this.baseURL = serverBase;
276 this.currentTimeMillis = System.currentTimeMillis();
277 this.notificationDepth = notificationDepth;
281 private void initBeans() {
282 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
283 ApplicationContext ctx = SpringContextAware.getApplicationContext();
284 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
286 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
287 setEdgeSerializer(es);
288 isDeltaEventsEnabled = Boolean.parseBoolean(
289 SpringContextAware.getApplicationContext().getEnvironment().getProperty("delta.events.enabled", FALSE));
290 isMultiTenancyEnabled = Boolean.parseBoolean(SpringContextAware.getApplicationContext().getEnvironment()
291 .getProperty("multi.tenancy.enabled", FALSE));
294 public void setEdgeSerializer(EdgeSerializer edgeSer) {
295 this.edgeSer = edgeSer;
298 public EdgeSerializer getEdgeSeriailizer() {
302 public void setEdgeIngestor(EdgeIngestor ei) {
306 public EdgeIngestor getEdgeIngestor() {
307 return this.edgeRules;
310 public Map<Vertex, Boolean> getUpdatedVertexes() {
311 return updatedVertexes;
314 public Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> getImpliedDeleteUriObjectPair() {
315 return impliedDeleteUriObjectPair;
318 public Set<String> getGroups() {
323 * Touch standard vertex properties.
326 * @param isNewVertex the is new vertex
328 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
329 String timeNowInSec = Long.toString(currentTimeMillis);
331 String uuid = UUID.randomUUID().toString();
332 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
333 v.property(AAIProperties.CREATED_TS, currentTimeMillis);
334 v.property(AAIProperties.AAI_UUID, uuid);
335 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
336 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
337 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
339 if (isDeltaEventsEnabled) {
340 standardVertexPropsDeltas(v, timeNowInSec);
342 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec);
343 v.property(AAIProperties.LAST_MOD_TS, currentTimeMillis);
344 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
348 private void standardVertexPropsDeltas(Vertex v, String timeNowInSec) {
349 String uri = v.property(AAIProperties.AAI_URI).value().toString();
350 long createdTs = (Long) v.property(AAIProperties.CREATED_TS).value();
351 DeltaAction objDeltaAction = createdTs == currentTimeMillis ? DeltaAction.CREATE : DeltaAction.UPDATE;
352 if (getObjectDeltas().containsKey(uri)) {
353 getObjectDeltas().get(uri).setAction(objDeltaAction);
356 addPropDelta(uri, AAIProperties.AAI_UUID,
357 PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.AAI_UUID).value()),
359 addPropDelta(uri, AAIProperties.NODE_TYPE,
360 PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.NODE_TYPE).value()),
362 addPropDelta(uri, AAIProperties.SOURCE_OF_TRUTH,
363 PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.SOURCE_OF_TRUTH).value()),
365 addPropDelta(uri, AAIProperties.CREATED_TS,
366 PropertyDeltaFactory.getDelta(DeltaAction.STATIC, v.property(AAIProperties.CREATED_TS).value()),
369 if (objDeltaAction.equals(DeltaAction.UPDATE)) {
370 addPropDelta(uri, AAIProperties.RESOURCE_VERSION, PropertyDeltaFactory.getDelta(objDeltaAction,
371 timeNowInSec, v.property(AAIProperties.RESOURCE_VERSION).value()), objDeltaAction);
372 addPropDelta(uri, AAIProperties.LAST_MOD_TS, PropertyDeltaFactory.getDelta(objDeltaAction,
373 currentTimeMillis, v.property(AAIProperties.LAST_MOD_TS).value()), objDeltaAction);
375 uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction,
376 this.sourceOfTruth, v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()),
379 addPropDelta(uri, AAIProperties.RESOURCE_VERSION,
380 PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.RESOURCE_VERSION).value()),
382 addPropDelta(uri, AAIProperties.LAST_MOD_TS,
383 PropertyDeltaFactory.getDelta(objDeltaAction, v.property(AAIProperties.LAST_MOD_TS).value()),
385 addPropDelta(uri, AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, PropertyDeltaFactory.getDelta(objDeltaAction,
386 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value()), objDeltaAction);
390 public Map<String, ObjectDelta> getObjectDeltas() {
394 private void addPropDelta(String uri, String prop, PropertyDelta delta, DeltaAction objDeltaAction) {
395 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri,
396 new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
397 objectDelta.addPropertyDelta(prop, delta);
398 objectDeltas.put(uri, objectDelta);
401 private void addRelationshipDelta(String uri, RelationshipDelta delta, DeltaAction objDeltaAction) {
402 ObjectDelta objectDelta = this.objectDeltas.getOrDefault(uri,
403 new ObjectDelta(uri, objDeltaAction, this.sourceOfTruth, this.currentTimeMillis));
404 objectDelta.addRelationshipDelta(delta);
405 objectDeltas.put(uri, objectDelta);
408 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
409 v.property(AAIProperties.NODE_TYPE, nodeType);
410 touchStandardVertexProperties(v, isNewVertex);
414 * Creates the new vertex.
416 * @param wrappedObject the wrapped object
419 public Vertex createNewVertex(Introspector wrappedObject) {
422 StopWatch.conditionalStart();
423 v = this.engine.tx().addVertex(wrappedObject.getDbName());
424 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
426 dbTimeMsecs += StopWatch.stopIfStarted();
434 * @param className the class name
438 * Removes the classpath from a class name
440 public String trimClassName(String className) {
441 String returnValue = "";
443 if (className.lastIndexOf('.') == -1) {
446 returnValue = className.substring(className.lastIndexOf('.') + 1);
456 * @param uriQuery the uri query
457 * @param identifier the identifier
458 * @throws SecurityException the security exception
459 * @throws IllegalArgumentException the illegal argument exception
460 * @throws AAIException the AAI exception
461 * @throws UnsupportedEncodingException the unsupported encoding exception
463 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier,
464 String requestContext) throws AAIException, UnsupportedEncodingException {
465 StopWatch.conditionalStart();
467 if (uriQuery.isDependent()) {
468 // try to find the parent
469 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
470 if (!vertices.isEmpty()) {
471 Vertex parent = vertices.get(0);
472 this.reflectDependentVertex(parent, v, obj, requestContext);
474 dbTimeMsecs += StopWatch.stopIfStarted();
475 throw new AAIException("AAI_6114",
476 "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
479 serializeSingleVertex(v, obj, requestContext);
482 } catch (SchemaViolationException e) {
483 dbTimeMsecs += StopWatch.stopIfStarted();
484 throw new AAIException("AAI_6117", e);
486 dbTimeMsecs += StopWatch.stopIfStarted();
489 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext)
490 throws UnsupportedEncodingException, AAIException {
491 StopWatch.conditionalStart();
493 boolean isTopLevel = obj.isTopLevel();
495 addUriIfNeeded(v, obj.getURI());
498 URI uri = this.getURIForVertex(v);
499 URIParser parser = new URIParser(this.loader, uri);
500 if (parser.validate()) {
501 addUriIfNeeded(v, uri.toString());
504 processObject(obj, v, requestContext);
505 } catch (SchemaViolationException e) {
506 throw new AAIException("AAI_6117", e);
508 dbTimeMsecs += StopWatch.stopIfStarted();
512 private void addUriIfNeeded(Vertex v, String uri) {
513 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
514 if (!uriProp.isPresent() || !uriProp.value().equals(uri)) {
515 v.property(AAIProperties.AAI_URI, uri);
525 * @throws IllegalArgumentException the illegal argument exception
526 * @throws SecurityException the security exception
527 * @throws AAIException the AAI exception
528 * @throws UnsupportedEncodingException the unsupported encoding exception
531 * Helper method for reflectToDb
532 * Handles all the property setting
534 private List<Vertex> processObject(Introspector obj, Vertex v, String requestContext)
535 throws UnsupportedEncodingException, AAIException {
536 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
537 properties.remove(AAIProperties.RESOURCE_VERSION);
538 List<Vertex> dependentVertexes = new ArrayList<>();
539 List<Vertex> processedVertexes = new ArrayList<>();
541 boolean isComplexType;
544 // If the notification depth is set to maximum
545 // this is the behavior of the expected clients
546 if (notificationDepth == AAIProperties.MAXIMUM_DEPTH) {
547 if (!obj.isContainer()) {
548 this.touchStandardVertexProperties(v, false);
551 this.executePreSideEffects(obj, v);
552 for (String property : properties) {
553 final String propertyType;
554 propertyType = obj.getType(property);
555 isComplexType = obj.isComplexType(property);
556 isListType = obj.isListType(property);
557 Object value = obj.getValue(property);
559 if (!(isComplexType || isListType)) {
560 boolean canModify = this.canModify(obj, property, requestContext);
563 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
564 String dbProperty = property;
565 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
566 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
568 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
569 // data linked properties are ephemeral
570 // they are populated dynamically on GETs
573 Object oldValue = v.property(dbProperty).orElse(null);
574 String uri = getURIForVertex(v).toString();
576 if (!value.equals(oldValue)) {
577 if (propertyType.toLowerCase().contains(".long")) {
578 v.property(dbProperty, new Integer(((Long) value).toString()));
580 v.property(dbProperty, value);
582 if (isDeltaEventsEnabled) {
583 createDeltaProperty(uri, value, dbProperty, oldValue);
585 this.updatedVertexes.putIfAbsent(v, false);
588 if (oldValue != null) {
589 v.property(dbProperty).remove();
590 if (isDeltaEventsEnabled) {
591 addPropDelta(uri, dbProperty,
592 PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldValue),
595 this.updatedVertexes.putIfAbsent(v, false);
599 } else if (isListType) {
600 List<Object> list = (List<Object>) value;
601 if (obj.isComplexGenericType(property)) {
603 for (Object o : list) {
604 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
605 child.setURIChain(obj.getURI());
606 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
611 if (isDeltaEventsEnabled) {
612 String uri = getURIForVertex(v).toString();
613 List<Object> oldVal = engine.getListProperty(v, property);
614 engine.setListProperty(v, property, list);
615 if (list == null || list.isEmpty()) { // property delete scenario, there is no new value
616 if (oldVal != null && !oldVal.isEmpty()) { // and there is an old value
617 addPropDelta(uri, property, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, oldVal),
620 } else { // is either a create or update and is handled by the called method
621 createDeltaProperty(uri, list, property, oldVal);
624 engine.setListProperty(v, property, list);
626 this.updatedVertexes.putIfAbsent(v, false);
629 // method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge
630 // back to this method
631 if (value != null) { // effectively ignore complex properties not included in the object we're
633 if (value.getClass().isArray()) {
635 int length = Array.getLength(value);
636 for (int i = 0; i < length; i++) {
637 Object arrayElement = Array.get(value, i);
638 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
639 child.setURIChain(obj.getURI());
640 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
643 } else if (!property.equals("relationship-list")) {
645 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
646 if (introspector.isContainer()) {
647 dependentVertexes.addAll(
648 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
649 introspector.setURIChain(obj.getURI());
651 processedVertexes.addAll(processObject(introspector, v, requestContext));
654 dependentVertexes.addAll(
655 this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
656 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
659 } else if (property.equals("relationship-list")) {
660 handleRelationships(obj, v);
665 this.writeThroughDefaults(v, obj);
666 /* handle those vertexes not touched */
667 for (Vertex toBeKept : processedVertexes) {
668 dependentVertexes.remove(toBeKept);
671 ImpliedDelete impliedDelete = new ImpliedDelete(engine, this);
672 List<Vertex> impliedDeleteVertices =
673 impliedDelete.execute(v.id(), sourceOfTruth, obj.getName(), dependentVertexes);
675 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
676 for (Vertex curVertex : impliedDeleteVertices) {
677 if (!curVertex.property("aai-uri").isPresent()) {
678 LOGGER.debug("Encountered an vertex {} with missing aai-uri", curVertex.id());
681 String curAaiUri = curVertex.<String>property(AAIProperties.AAI_URI).value();
682 Introspector curObj = this.getLatestVersionView(curVertex, notificationDepth);
684 LinkedHashMap<String, Introspector> curObjRelated = new LinkedHashMap<>();
686 if (!curObj.isTopLevel()) {
688 .putAll(this.getRelatedObjects(engine.getQueryEngine(), curVertex, curObj, this.loader));
691 if (!impliedDeleteUriObjectPair.containsKey(curAaiUri)) {
692 impliedDeleteUriObjectPair.put(curAaiUri, new Pair<>(curObj, curObjRelated));
697 impliedDelete.delete(impliedDeleteVertices);
699 // touch svp using vertex list for what changed
700 // if the notification depth is zero
701 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
702 this.updatedVertexes.entrySet().stream().filter(e -> !e.getValue())
703 .filter(e -> !edgeVertexes.contains(e.getKey())).forEach(e -> {
704 this.touchStandardVertexProperties(e.getKey(), false);
708 this.executePostSideEffects(obj, v);
709 return processedVertexes;
712 private void createDeltaProperty(String uri, Object value, String dbProperty, Object oldValue) {
713 if (oldValue == null) {
714 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.CREATE, value), DeltaAction.UPDATE);
716 addPropDelta(uri, dbProperty, PropertyDeltaFactory.getDelta(DeltaAction.UPDATE, value, oldValue),
721 public HashMap<String, Introspector> getRelatedObjects(QueryEngine queryEngine, Vertex v, Introspector obj,
723 throws IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIException {
725 HashMap<String, Introspector> relatedVertices = new HashMap<>();
726 VertexProperty<Object> aaiUriProperty = v.property(AAIProperties.AAI_URI);
728 if (!aaiUriProperty.isPresent()) {
729 if (LOGGER.isDebugEnabled()) {
730 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
734 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
736 return relatedVertices;
739 String aaiUri = aaiUriProperty.value().toString();
741 if (!obj.isTopLevel()) {
742 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
743 List<Vertex> vertexChain;
744 // If the uriList is null then there is something wrong with converting the uri
745 // into a list of aai-uris so falling back to the old mechanism for finding parents
746 if (uriList == null) {
748 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
749 vertexChain = queryEngine.findParents(v);
750 } else if (uriList.length == 1) {
751 // If the uri list is size 1 the only uri in the list is the one represented by v thus no need to query
752 vertexChain = Collections.singletonList(v);
754 // the uriList at element 0 is the node in question and should not be included in the vertex chain
756 vertexChain = queryEngine.findParents(Arrays.copyOfRange(uriList, 1, uriList.length));
757 // inject v into start of vertexChain
758 vertexChain.add(0, v);
760 for (Vertex vertex : vertexChain) {
762 final Introspector vertexObj = this.getVertexProperties(vertex);
763 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
764 } catch (AAIUnknownObjectException e) {
765 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
770 final Introspector vertexObj = this.getVertexProperties(v);
771 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
772 } catch (AAIUnknownObjectException e) {
773 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
777 return relatedVertices;
781 * Given an uri, introspector object and loader object
782 * it will check if the obj is top level object if it is,
783 * it will return immediately returning the uri passed in
784 * If it isn't, it will go through, get the uriTemplate
785 * from the introspector object and get the count of "/"s
786 * and remove that part of the uri using substring
787 * and keep doing that until the current object is top level
788 * Also added the max depth just so worst case scenario
789 * Then keep adding aai-uri to the list include the aai-uri passed in
790 * Convert that list into an array and return it
797 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
799 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
800 * it converts to /vservers/vserver
802 * lastIndexOf /vservers/vserver in
803 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
808 * Use substring to get the string from 0 to that lastIndexOf
809 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
811 * From this new aai-uri, generate a introspector from the URITOObject class
812 * and keep doing this until you
816 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
817 * @param obj - introspector object of the given starting vertex
818 * @param loader - Type of loader which will always be MoxyLoader to support model driven
819 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
820 * @throws UnsupportedEncodingException
821 * @throws AAIException
823 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
824 throws UnsupportedEncodingException, AAIException {
826 List<String> uriList = new ArrayList<>();
828 String truncatedUri = aaiUri;
829 int depth = AAIProperties.MAXIMUM_DEPTH;
830 uriList.add(truncatedUri);
832 while (depth >= 0 && !obj.isTopLevel()) {
833 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
835 if (template == null) {
836 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
840 int templateCount = StringUtils.countMatches(template, "/");
841 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
843 if (templateCount > truncatedUriCount) {
844 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
848 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
849 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
850 uriList.add(truncatedUri);
851 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
855 return uriList.toArray(new String[0]);
859 * Handle relationships.
862 * @param vertex the vertex
863 * @throws SecurityException the security exception
864 * @throws IllegalArgumentException the illegal argument exception
865 * @throws UnsupportedEncodingException the unsupported encoding exception
866 * @throws AAIException the AAI exception
869 * Handles the explicit relationships defined for an obj
871 private void handleRelationships(Introspector obj, Vertex vertex)
872 throws UnsupportedEncodingException, AAIException {
874 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
875 processRelationshipList(wrappedRl, vertex);
880 * Process relationship list.
882 * @param wrapped the wrapped
884 * @throws UnsupportedEncodingException the unsupported encoding exception
885 * @throws AAIException the AAI exception
887 private void processRelationshipList(Introspector wrapped, Vertex v)
888 throws UnsupportedEncodingException, AAIException {
890 List<Object> relationships = wrapped.getValue("relationship");
891 String mainUri = getURIForVertex(v).toString();
892 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
893 EdgeRuleQuery.Builder cousinQueryBuilder =
894 new EdgeRuleQuery.Builder(aNodeType).edgeType(EdgeType.COUSIN).version(wrapped.getVersion());
895 EdgeRuleQuery.Builder treeQueryBuilder =
896 new EdgeRuleQuery.Builder(aNodeType).edgeType(EdgeType.TREE).version(wrapped.getVersion());
898 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
900 Set<Pair<String, String>> cousinUriAndLabels = new LinkedHashSet<>();
901 for (Object relationship : relationships) {
904 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
905 String relUri = urlToUri(new RelationshipToURI(loader, wrappedRel).getUri().toString());
907 if (relUri.startsWith("/vnf/")) {
908 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
909 List<Vertex> results = parser.getQueryBuilder().toList();
910 if (results.isEmpty()) {
911 final AAIException ex =
912 new AAIException(AAI_6129, String.format("Node of type %s. Could not find object at: %s",
913 parser.getResultType(), parser.getUri()));
914 ex.getTemplateVars().add(parser.getResultType());
915 ex.getTemplateVars().add(parser.getUri().toString());
918 // still an issue if there's more than one
919 if (results.get(0).property(AAIProperties.AAI_URI).isPresent()) {
920 relUri = results.get(0).value(AAIProperties.AAI_URI);
922 LOGGER.warn("Not processing the vertex {} because its missing required property aai-uri",
923 results.get(0).id());
929 if (wrappedRel.getValue(RELATIONSHIP_LABEL) != null) {
930 label = wrappedRel.getValue(RELATIONSHIP_LABEL);
932 URIToObject uriToObject = new URIToObject(loader, URI.create(relUri));
933 String bNodeType = uriToObject.getEntityName();
934 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).build();
935 if (!edgeIngestor.hasRule(ruleQuery)) {
936 EdgeRuleQuery treeQuery = treeQueryBuilder.to(bNodeType).build();
937 if (edgeIngestor.hasRule(treeQuery)) {
938 throw new AAIException(AAI_6145); // attempted to create cousin edge for a parent-child edge
941 throw new AAIException("AAI_6120",
942 String.format("No EdgeRule found for passed nodeTypes: %s, %s.", aNodeType, bNodeType));
945 final List<EdgeRule> rules = new ArrayList<>(edgeIngestor.getRules(ruleQuery).values());
946 if (rules.size() == 1) {
947 label = rules.get(0).getLabel();
949 Optional<EdgeRule> defaultRule = rules.stream().filter(EdgeRule::isDefault).findFirst();
950 if (defaultRule.isPresent()) {
951 label = defaultRule.get().getLabel();
953 throw new AAIException(AAI_6145);
956 } catch (EdgeRuleNotFoundException ea) {
957 throw new AAIException(AAI_6145, ea);
961 cousinUriAndLabels.add(Pair.with(relUri, label));
964 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
965 Set<Path> toRemove = new HashSet<>();
967 // for each path 3 things can happen:
968 // 1. The edge rule that created it is not in this version no action is to be taken on that edge
969 // 2. The edge rule exits in this version it's included in the request the edge is left alone
970 // 3. The edge rule exits in this version and is not included in the request it is marked for removal
971 for (Path path : paths) {
972 if (path.size() < 3) {
977 // v ----related-to--> otherV
978 // In the above case,
979 // path objects get(0) returns vertex v
980 // path objects.get(1) returns edge related-to
981 // path objects.get(2) returns vertex otherV
982 Vertex otherV = path.get(2);
985 if (otherV.property(AAIProperties.AAI_URI).isPresent()) {
986 bUri = otherV.value(AAIProperties.AAI_URI);
990 String edgeLabel = path.<Edge>get(1).label();
992 Pair<String, String> key = Pair.with(bUri, edgeLabel);
993 if (cousinUriAndLabels.contains(key)) {
994 cousinUriAndLabels.remove(key);
997 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
998 bNodeType = otherV.property(AAIProperties.NODE_TYPE).value().toString();
1002 EdgeRuleQuery ruleQuery = cousinQueryBuilder.to(bNodeType).label(edgeLabel).build();
1003 if (edgeIngestor.hasRule(ruleQuery)) {
1010 Set<Pair<Vertex, String>> toBeCreated = new HashSet<>();
1011 for (Pair<String, String> cousinUriAndLabel : cousinUriAndLabels) {
1013 Vertex cousinVertex;
1014 String label = cousinUriAndLabel.getValue1();
1015 String cousinUri = cousinUriAndLabel.getValue0();
1016 QueryParser parser = engine.getQueryBuilder().createQueryFromURI(URI.create(cousinUri));
1018 List<Vertex> results = parser.getQueryBuilder().toList();
1019 if (results.isEmpty()) {
1020 final AAIException ex = new AAIException(AAI_6129,
1021 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1022 ex.getTemplateVars().add(parser.getResultType());
1023 ex.getTemplateVars().add(parser.getUri().toString());
1026 // still an issue if there's more than one
1027 cousinVertex = results.get(0);
1030 if (cousinVertex != null) {
1031 String vType = (String) v.property(AAIProperties.NODE_TYPE).value();
1032 String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value();
1033 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
1035 if (!edgeRules.hasRule(baseQ.build())) {
1036 throw new AAIException("AAI_6120",
1037 String.format("No EdgeRule found for passed nodeTypes: %s, %s%s.", aNodeType, cousinType,
1038 label != null ? (" with label " + label) : ""));
1039 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build())
1040 && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
1041 throw new AAIException(AAI_6145);
1044 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
1047 toBeCreated.add(Pair.with(cousinVertex, label));
1052 for (Path path : toRemove) {
1053 if (isDeltaEventsEnabled) {
1054 deltaForEdge(mainUri, path.get(1), DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1056 this.updatedVertexes.putIfAbsent(v, false);
1057 this.edgeVertexes.add(path.get(2));
1058 path.<Edge>get(1).remove();
1061 for (Pair<Vertex, String> create : toBeCreated) {
1063 Edge e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), v, create.getValue0(),
1064 create.getValue1());
1065 if (isDeltaEventsEnabled) {
1066 deltaForEdge(mainUri, e, DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1068 this.updatedVertexes.putIfAbsent(v, false);
1069 this.edgeVertexes.add(create.getValue0());
1070 } catch (NoEdgeRuleFoundException ex) {
1071 throw new AAIException(AAI_6129, ex);
1076 private void deltaForEdge(String mainUri, Edge edge, DeltaAction edgeAction, DeltaAction mainAction) {
1077 RelationshipDelta relationshipDelta =
1078 new RelationshipDelta(edgeAction, edge.inVertex().property(AAIProperties.AAI_UUID).value().toString(),
1079 edge.outVertex().property(AAIProperties.AAI_UUID).value().toString(),
1080 edge.inVertex().property(AAIProperties.AAI_URI).value().toString(),
1081 edge.outVertex().property(AAIProperties.AAI_URI).value().toString(), edge.label());
1082 edge.properties().forEachRemaining(p -> relationshipDelta.addProp(p.key(), p.value().toString()));
1083 addRelationshipDelta(mainUri, relationshipDelta, mainAction);
1087 * Write through defaults.
1090 * @param obj the obj
1091 * @throws AAIUnknownObjectException
1093 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
1094 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
1095 if (latest != null) {
1096 Set<String> required = latest.getRequiredProperties();
1098 for (String field : required) {
1099 String defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
1100 if (defaultValue != null) {
1101 Object vertexProp = v.property(field).orElse(null);
1102 if (vertexProp == null) {
1103 v.property(field, defaultValue);
1112 * Reflect dependent vertex.
1115 * @param dependentObj the dependent obj
1116 * @return the vertex
1117 * @throws IllegalArgumentException the illegal argument exception
1118 * @throws SecurityException the security exception
1119 * @throws AAIException the AAI exception
1120 * @throws UnsupportedEncodingException the unsupported encoding exception
1121 * @throws AAIUnknownObjectException
1123 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext)
1124 throws AAIException, UnsupportedEncodingException {
1126 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
1127 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
1128 query.createKeyQuery(dependentObj);
1130 List<Vertex> items = query.toList();
1132 Vertex dependentVertex;
1133 if (items.size() == 1) {
1134 dependentVertex = items.get(0);
1135 this.verifyResourceVersion("update", dependentObj.getDbName(),
1136 dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
1137 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1139 this.verifyResourceVersion("create", dependentObj.getDbName(), "",
1140 dependentObj.getValue(AAIProperties.RESOURCE_VERSION), dependentObj.getURI());
1141 dependentVertex = createNewVertex(dependentObj);
1144 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
1149 * Reflect dependent vertex.
1151 * @param parent the parent
1152 * @param child the child
1153 * @param obj the obj
1154 * @return the vertex
1155 * @throws IllegalArgumentException the illegal argument exception
1156 * @throws SecurityException the security exception
1157 * @throws AAIException the AAI exception
1158 * @throws UnsupportedEncodingException the unsupported encoding exception
1159 * @throws AAIUnknownObjectException
1161 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext)
1162 throws AAIException, UnsupportedEncodingException {
1164 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
1165 if (parentUri != null) {
1168 addUriIfNeeded(child, parentUri + uri);
1170 processObject(obj, child, requestContext);
1173 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
1176 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
1177 if (canBeLinked != null && canBeLinked.equals("true")) {
1178 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class)
1179 .createLoaderForVersion(introspectionType, getVerForContext(requestContext));
1180 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent)
1181 .createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
1183 child.property(AAIProperties.LINKED, true);
1186 e = edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
1187 if (isDeltaEventsEnabled) {
1188 deltaForEdge(child.property(AAIProperties.AAI_URI).value().toString(), e, DeltaAction.CREATE_REL,
1189 DeltaAction.CREATE);
1196 private SchemaVersion getVerForContext(String requestContext) {
1197 Pattern pattern = Pattern.compile("v[0-9]+");
1198 Matcher m = pattern.matcher(requestContext);
1200 return this.version;
1202 return new SchemaVersion(requestContext);
1209 * @param vertices the vertices
1210 * @param obj the obj
1211 * @param depth the depth
1212 * @param cleanUp the clean up
1213 * @return the introspector
1214 * @throws AAIException the AAI exception
1215 * @throws IllegalArgumentException the illegal argument exception
1216 * @throws SecurityException the security exception
1217 * @throws UnsupportedEncodingException the unsupported encoding exception
1218 * @throws AAIUnknownObjectException
1219 * @throws URISyntaxException
1221 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1222 String cleanUp, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
1223 final int internalDepth;
1224 if (depth == Integer.MAX_VALUE) {
1225 internalDepth = depth--;
1227 internalDepth = depth;
1229 StopWatch.conditionalStart();
1230 if (vertices.size() > 1 && !obj.isContainer()) {
1231 dbTimeMsecs += StopWatch.stopIfStarted();
1232 throw new AAIException("AAI_6136",
1233 "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
1234 } else if (obj.isContainer()) {
1235 final List<Object> getList;
1236 String listProperty = null;
1237 for (String property : obj.getProperties()) {
1238 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
1239 listProperty = property;
1243 final String propertyName = listProperty;
1244 getList = obj.getValue(listProperty);
1247 * This is an experimental multithreading experiment
1250 ExecutorService pool = GetAllPool.getInstance().getPool();
1252 List<Future<Object>> futures = new ArrayList<>();
1254 for (Vertex v : vertices) {
1255 AaiCallable<Object> task = new AaiCallable<Object>() {
1257 public Object process() throws UnsupportedEncodingException, AAIException {
1258 Set<Vertex> seen = new HashSet<>();
1259 Introspector childObject;
1260 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
1261 dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp, isSkipRelatedTo);
1262 return childObject.getUnderlyingObject();
1265 futures.add(pool.submit(task));
1268 for (Future<Object> future : futures) {
1270 getList.add(future.get());
1271 } catch (InterruptedException e) {
1272 dbTimeMsecs += StopWatch.stopIfStarted();
1273 Thread.currentThread().interrupt();
1274 throw new AAIException("AAI_4000", e);
1275 } catch (ExecutionException e) {
1276 dbTimeMsecs += StopWatch.stopIfStarted();
1277 throw new AAIException("AAI_4000", e);
1280 } else if (vertices.size() == 1) {
1281 Set<Vertex> seen = new HashSet<>();
1282 dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp, isSkipRelatedTo);
1287 dbTimeMsecs += StopWatch.stopIfStarted();
1294 * @param vertices the vertices
1295 * @param obj the obj
1296 * @param depth the depth
1297 * @param cleanUp the clean up
1298 * @return the introspector
1299 * @throws AAIException the AAI exception
1300 * @throws IllegalArgumentException the illegal argument exception
1301 * @throws SecurityException the security exception
1302 * @throws UnsupportedEncodingException the unsupported encoding exception
1303 * @throws AAIUnknownObjectException
1304 * @throws URISyntaxException
1306 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly,
1307 String cleanUp) throws UnsupportedEncodingException, AAIException {
1308 return dbToObject(vertices, obj, depth, nodeOnly, cleanUp, false);
1314 * @param obj the obj
1316 * @param seen the seen
1317 * @param depth the depth
1318 * @param cleanUp the clean up
1319 * @return the introspector
1320 * @throws IllegalArgumentException the illegal argument exception
1321 * @throws SecurityException the security exception
1322 * @throws UnsupportedEncodingException the unsupported encoding exception
1323 * @throws AAIException the AAI exception
1324 * @throws AAIUnknownObjectException
1325 * @throws URISyntaxException
1327 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1328 String cleanUp) throws AAIException, UnsupportedEncodingException {
1329 return dbToObject(obj, v, seen, depth, nodeOnly, cleanUp, false);
1335 * @param obj the obj
1337 * @param seen the seen
1338 * @param depth the depth
1339 * @param cleanUp the clean up
1340 * @return the introspector
1341 * @throws IllegalArgumentException the illegal argument exception
1342 * @throws SecurityException the security exception
1343 * @throws UnsupportedEncodingException the unsupported encoding exception
1344 * @throws AAIException the AAI exception
1345 * @throws AAIUnknownObjectException
1346 * @throws URISyntaxException
1348 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly,
1349 String cleanUp, boolean isSkipRelatedTo) throws AAIException, UnsupportedEncodingException {
1357 boolean modified = false;
1358 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1359 List<Object> getList = null;
1361 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1362 this.copySimpleProperty(property, obj, v);
1365 if (obj.isComplexType(property)) {
1366 /* container case */
1368 if (!property.equals("relationship-list") && depth >= 0) {
1369 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1370 Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp);
1371 if (result != null) {
1372 obj.setValue(property, argumentObject.getUnderlyingObject());
1375 } else if (property.equals("relationship-list") && !nodeOnly) {
1376 /* relationships need to be handled correctly */
1377 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1378 relationshipList = createRelationshipList(v, relationshipList, cleanUp, isSkipRelatedTo);
1379 if (relationshipList != null) {
1380 obj.setValue(property, relationshipList.getUnderlyingObject());
1385 } else if (obj.isListType(property)) {
1387 if (property.equals("any")) {
1390 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1391 if (obj.isComplexGenericType(property) && depth >= 0) {
1392 final String childDbName = convertFromCamelCase(genericType);
1393 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1397 rule = edgeRules.getRule(
1398 new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
1399 } catch (EdgeRuleNotFoundException e) {
1400 throw new NoEdgeRuleFoundException(e);
1401 } catch (AmbiguousRuleChoiceException e) {
1402 throw new MultipleEdgeRuleFoundException(e);
1404 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
1406 Direction ruleDirection = rule.getDirection();
1407 List<Vertex> verticesList = new ArrayList<>();
1408 v.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
1409 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
1410 verticesList.add(vertex);
1413 if (!verticesList.isEmpty()) {
1414 getList = obj.getValue(property);
1417 for (Vertex childVertex : verticesList) {
1418 if (!seen.contains(childVertex)) {
1419 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1421 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly,
1422 cleanUp, isSkipRelatedTo);
1423 if (result != null && getList != null) {
1424 getList.add(argumentObject.getUnderlyingObject());
1429 LOGGER.warn("Cycle found while serializing vertex id={}",
1430 childVertex.id().toString());
1433 if (processed == 0) {
1434 // vertices were all seen, reset the list
1437 if (processed > 0) {
1441 } else if (obj.isSimpleGenericType(property)) {
1442 List<Object> temp = this.engine.getListProperty(v, property);
1444 getList = (List<Object>) obj.getValue(property);
1445 getList.addAll(temp);
1453 // no changes were made to this obj, discard the instance
1457 this.enrichData(obj, v);
1462 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
1463 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1464 if (nodeType == null) {
1465 throw new AAIException("AAI_6143");
1468 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1469 Set<Vertex> seen = new HashSet<>();
1471 StopWatch.conditionalStart();
1472 this.dbToObject(obj, v, seen, depth, true, FALSE);
1473 dbTimeMsecs += StopWatch.stopIfStarted();
1478 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
1479 return getLatestVersionView(v, AAIProperties.MAXIMUM_DEPTH);
1482 public Introspector getLatestVersionView(Vertex v, int depth) throws AAIException, UnsupportedEncodingException {
1483 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1484 if (nodeType == null) {
1485 throw new AAIException("AAI_6143");
1487 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
1488 Set<Vertex> seen = new HashSet<>();
1489 StopWatch.conditionalStart();
1490 this.dbToObject(obj, v, seen, depth, false, FALSE);
1491 dbTimeMsecs += StopWatch.stopIfStarted();
1496 * Copy simple property.
1498 * @param property the property
1499 * @param obj the obj
1501 * @throws IllegalArgumentException the illegal argument exception
1502 * @throws SecurityException the security exception
1504 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
1505 final Object temp = getProperty(obj, property, v);
1507 obj.setValue(property, temp);
1511 public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v) {
1513 Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible());
1514 String[] simplePropsArray = new String[simpleProperties.size()];
1515 simplePropsArray = simpleProperties.toArray(simplePropsArray);
1517 Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2);
1519 v.properties(simplePropsArray).forEachRemaining(vp -> simplePropsHashMap.put(vp.key(), vp.value()));
1521 return simplePropsHashMap;
1524 public Introspector dbToRelationshipObject(Vertex v, boolean isSkipRelatedTo)
1525 throws UnsupportedEncodingException, AAIException {
1526 Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list");
1527 relationshipList = createRelationshipList(v, relationshipList, FALSE, isSkipRelatedTo);
1528 return relationshipList;
1532 * Creates the relationship list.
1535 * @param obj the obj
1536 * @param cleanUp the clean up
1537 * @return the object
1538 * @throws IllegalArgumentException the illegal argument exception
1539 * @throws SecurityException the security exception
1540 * @throws UnsupportedEncodingException the unsupported encoding exception
1541 * @throws AAIException the AAI exception
1542 * @throws URISyntaxException
1544 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp)
1545 throws UnsupportedEncodingException, AAIException {
1546 // default boolean value for isSkipRelatedTo is false
1547 return createRelationshipList(v, obj, cleanUp, false);
1551 * Creates the relationship list.
1554 * @param obj the obj
1555 * @param cleanUp the clean up
1556 * @param isSkipRelatedTo to determine adding related-to-property in response
1557 * @return the object
1558 * @throws IllegalArgumentException the illegal argument exception
1559 * @throws SecurityException the security exception
1560 * @throws UnsupportedEncodingException the unsupported encoding exception
1561 * @throws AAIException the AAI exception
1562 * @throws URISyntaxException
1564 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp, boolean isSkipRelatedTo)
1565 throws UnsupportedEncodingException, AAIException {
1567 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
1568 VertexProperty<Object> nodeTypeProperty = v.property(AAIProperties.NODE_TYPE);
1570 if (!nodeTypeProperty.isPresent()) {
1571 LOGGER.warn("Not processing the vertex {} because its missing required property aai-node-type", v.id());
1575 List<Path> paths = this.engine.getQueryEngine().findCousinsAsPath(v);
1577 String aNodeType = v.property(AAIProperties.NODE_TYPE).value().toString();
1579 EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class);
1581 EdgeRuleQuery.Builder queryBuilder =
1582 new EdgeRuleQuery.Builder(aNodeType).edgeType(EdgeType.COUSIN).version(obj.getVersion());
1584 for (Path path : paths) {
1585 if (path.size() < 3) {
1590 // v ----related-to--> otherV
1591 // In the above case,
1592 // path objects get(0) returns vertex v
1593 // path objects.get(1) returns edge related-to
1594 // path objects.get(2) returns vertex otherV
1595 Edge edge = path.get(1);
1596 Vertex otherV = path.get(2);
1598 // TODO: Come back and revisit this code
1599 // Create a query based on the a nodetype and b nodetype
1600 // which is also a cousin edge and ensure the version
1601 // is used properly so for example in order to be backwards
1602 // compatible if we had allowed a edge between a and b
1603 // in a previous release and we decided to remove it from
1604 // the edge rules in the future we can display the edge
1605 // only for the older apis and the new apis if the edge rule
1606 // is removed will not be seen in the newer version of the API
1609 if (otherV.property(AAIProperties.NODE_TYPE).isPresent()) {
1610 bNodeType = otherV.value(AAIProperties.NODE_TYPE);
1615 String edgeLabel = edge.label();
1616 EdgeRuleQuery ruleQuery = queryBuilder.to(bNodeType).label(edgeLabel).build();
1618 if (!edgeIngestor.hasRule(ruleQuery)) {
1619 LOGGER.debug("Caught an edge rule not found for query {}", ruleQuery);
1623 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
1624 Object result = processEdgeRelationship(relationshipObj, otherV, cleanUp, edgeLabel, isSkipRelatedTo);
1625 if (result != null) {
1626 relationshipObjList.add(result);
1631 if (relationshipObjList.isEmpty()) {
1639 * Process edge relationship.
1641 * @param relationshipObj the relationship obj
1642 * @param edgeLabel the edge's label
1643 * @param cleanUp the clean up
1644 * @return the object
1645 * @throws IllegalArgumentException the illegal argument exception
1646 * @throws SecurityException the security exception
1647 * @throws UnsupportedEncodingException the unsupported encoding exception
1648 * @throws AAIUnknownObjectException
1649 * @throws URISyntaxException
1651 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp,
1652 String edgeLabel, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIUnknownObjectException {
1654 VertexProperty<Object> aaiUriProperty = cousin.property("aai-uri");
1656 if (!aaiUriProperty.isPresent()) {
1660 URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build();
1662 URIToRelationshipObject uriParser;
1663 Introspector result;
1665 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1666 result = uriParser.getResult();
1667 } catch (AAIException | URISyntaxException e) {
1668 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion()
1669 + " (bad vertex ID=" + ": " + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1673 VertexProperty<Object> cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE);
1675 if (cousinVertexNodeType.isPresent()) {
1676 String cousinType = cousinVertexNodeType.value().toString();
1677 if (namedPropNodes.contains(cousinType) && !isSkipRelatedTo) {
1678 this.addRelatedToProperty(result, cousin, cousinType);
1682 if (edgeLabel != null && result.hasProperty(RELATIONSHIP_LABEL)) {
1683 result.setValue(RELATIONSHIP_LABEL, edgeLabel);
1686 return result.getUnderlyingObject();
1690 * Gets the URI for vertex.
1693 * @return the URI for vertex
1694 * @throws IllegalArgumentException the illegal argument exception
1695 * @throws SecurityException the security exception
1696 * @throws UnsupportedEncodingException the unsupported encoding exception
1697 * @throws AAIUnknownObjectException
1699 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1701 return getURIForVertex(v, false);
1704 public URI getURIForVertex(Vertex v, boolean overwrite) {
1705 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1707 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1709 if (aaiUri != null && !overwrite) {
1710 uri = UriBuilder.fromPath(aaiUri).build();
1716 public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType)
1717 throws AAIUnknownObjectException {
1722 obj = this.loader.introspectorFromName(cousinType);
1723 } catch (AAIUnknownObjectException ex) {
1724 if (LOGGER.isTraceEnabled()) {
1725 LOGGER.trace("Encountered unknown object exception when trying to load nodetype of {} for vertex id {}",
1726 cousinType, cousinVertex.id());
1731 String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS);
1732 List<Introspector> relatedToProperties = new ArrayList<>();
1734 if (nameProps != null) {
1735 String[] props = nameProps.split(",");
1736 for (String prop : props) {
1737 final Object temp = getProperty(obj, prop, cousinVertex);
1738 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1739 relatedTo.setValue("property-key", cousinType + "." + prop);
1740 relatedTo.setValue("property-value", temp);
1741 relatedToProperties.add(relatedTo);
1745 if (!relatedToProperties.isEmpty()) {
1746 List<Object> relatedToList = relationship.getValue("related-to-property");
1747 for (Introspector introspector : relatedToProperties) {
1748 relatedToList.add(introspector.getUnderlyingObject());
1754 private Object getProperty(Introspector obj, String prop, Vertex vertex) {
1756 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(prop);
1757 String dbPropertyName = prop;
1759 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
1760 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
1763 return vertex.<Object>property(dbPropertyName).orElse(null);
1769 * @param relationship the relationship
1770 * @param inputVertex the input vertex
1771 * @return true, if successful
1772 * @throws UnsupportedEncodingException the unsupported encoding exception
1773 * @throws AAIException the AAI exception
1775 public Vertex createEdge(Introspector relationship, Vertex inputVertex)
1776 throws UnsupportedEncodingException, AAIException {
1778 Vertex relatedVertex;
1779 StopWatch.conditionalStart();
1780 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1782 String label = null;
1783 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1784 label = relationship.getValue(RELATIONSHIP_LABEL);
1787 List<Vertex> results = parser.getQueryBuilder().toList();
1788 if (results.isEmpty()) {
1789 dbTimeMsecs += StopWatch.stopIfStarted();
1790 AAIException e = new AAIException(AAI_6129,
1791 "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1792 e.getTemplateVars().add(parser.getResultType());
1793 e.getTemplateVars().add(parser.getUri().toString());
1796 // still an issue if there's more than one
1797 relatedVertex = results.get(0);
1800 if (relatedVertex != null) {
1804 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1806 e = edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1807 if (isDeltaEventsEnabled) {
1808 deltaForEdge(inputVertex.property(AAIProperties.AAI_URI).value().toString(), e,
1809 DeltaAction.CREATE_REL, DeltaAction.UPDATE);
1812 // attempted to link two vertexes already linked
1815 dbTimeMsecs += StopWatch.stopIfStarted();
1819 dbTimeMsecs += StopWatch.stopIfStarted();
1820 return relatedVertex;
1824 * Gets all the edges between of the type with the specified label.
1826 * @param aVertex the out vertex
1827 * @param bVertex the in vertex
1828 * @return the edges between
1830 private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) {
1834 if (bVertex != null) {
1835 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1836 if (EdgeType.TREE.equals(type)) {
1837 GraphTraversal<Vertex, Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex);
1838 if (edgeRule.getDirection().equals(Direction.IN)) {
1839 findEdgesBetween = findVertex.outE(edgeRule.getLabel())
1840 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1841 .not(__.has(EdgeField.PRIVATE.toString(), true));
1843 findEdgesBetween = findVertex.inE(edgeRule.getLabel())
1844 .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains())
1845 .not(__.has(EdgeField.PRIVATE.toString(), true));
1847 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1);
1849 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel());
1850 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1851 .not(__.has(EdgeField.PRIVATE.toString(), true));
1852 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1);
1854 List<Edge> list = findEdgesBetween.toList();
1855 if (!list.isEmpty()) {
1856 result = list.get(0);
1864 * Gets all the edges string between of the type.
1866 * @param aVertex the out vertex
1867 * @param bVertex the in vertex
1868 * @return the edges between
1869 * @throws NoEdgeRuleFoundException
1871 private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1873 List<String> result = new ArrayList<>();
1875 if (bVertex != null) {
1876 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1877 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1878 if (EdgeType.TREE.equals(type)) {
1879 findEdgesBetween = findEdgesBetween.not(__.or(__.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1880 __.has(EdgeField.PRIVATE.toString(), true)));
1882 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE")
1883 .not(__.has(EdgeField.PRIVATE.toString(), true));
1885 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1886 result = findEdgesBetween.label().toList();
1892 * Gets all the edges between the vertexes with the label and type.
1894 * @param vertexOut the out vertex
1895 * @param vertexIn the in vertex
1897 * @return the edges between
1898 * @throws AAIException the AAI exception
1900 private Edge getEdgesBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1904 if (vertexIn != null) {
1905 String aType = vertexOut.<String>property(AAIProperties.NODE_TYPE).value();
1906 String bType = vertexIn.<String>property(AAIProperties.NODE_TYPE).value();
1907 EdgeRuleQuery query = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1910 rule = edgeRules.getRule(query);
1911 } catch (EdgeRuleNotFoundException e) {
1912 throw new NoEdgeRuleFoundException(e);
1913 } catch (AmbiguousRuleChoiceException e) {
1914 throw new MultipleEdgeRuleFoundException(e);
1916 edge = this.getEdgeBetweenWithLabel(type, vertexOut, vertexIn, rule);
1923 * Gets the edge between with the label and edge type.
1925 * @param vertexOut the out vertex
1926 * @param vertexIn the in vertex
1928 * @return the edge between
1929 * @throws AAIException the AAI exception
1930 * @throws NoEdgeRuleFoundException
1932 public Edge getEdgeBetween(EdgeType type, Vertex vertexOut, Vertex vertexIn, String label) throws AAIException {
1934 StopWatch.conditionalStart();
1935 if (vertexIn != null) {
1937 Edge edge = this.getEdgesBetween(type, vertexOut, vertexIn, label);
1939 dbTimeMsecs += StopWatch.stopIfStarted();
1944 dbTimeMsecs += StopWatch.stopIfStarted();
1948 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1949 return this.getEdgeBetween(type, aVertex, bVertex, null);
1955 * @param relationship the relationship
1956 * @param inputVertex the input vertex
1957 * @return true, if successful
1958 * @throws UnsupportedEncodingException the unsupported encoding exception
1959 * @throws AAIException the AAI exception
1961 public Optional<Vertex> deleteEdge(Introspector relationship, Vertex inputVertex)
1962 throws UnsupportedEncodingException, AAIException {
1964 Vertex relatedVertex;
1965 StopWatch.conditionalStart();
1966 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1968 List<Vertex> results = parser.getQueryBuilder().toList();
1970 String label = null;
1971 if (relationship.hasProperty(RELATIONSHIP_LABEL)) {
1972 label = relationship.getValue(RELATIONSHIP_LABEL);
1975 if (results.isEmpty()) {
1976 dbTimeMsecs += StopWatch.stopIfStarted();
1977 return Optional.empty();
1980 relatedVertex = results.get(0);
1983 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1984 } catch (NoEdgeRuleFoundException e) {
1985 dbTimeMsecs += StopWatch.stopIfStarted();
1986 throw new AAIException(AAI_6129, e);
1989 if (isDeltaEventsEnabled) {
1990 String mainUri = inputVertex.property(AAIProperties.AAI_URI).value().toString();
1991 deltaForEdge(mainUri, edge, DeltaAction.DELETE_REL, DeltaAction.UPDATE);
1994 dbTimeMsecs += StopWatch.stopIfStarted();
1995 return Optional.of(relatedVertex);
1997 dbTimeMsecs += StopWatch.stopIfStarted();
1998 return Optional.empty();
2004 * Delete with traversal.
2006 * @param startVertex the start vertex
2008 public void deleteWithTraversal(Vertex startVertex) {
2009 StopWatch.conditionalStart();
2010 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
2011 this.delete(results);
2015 * Removes the list of vertexes from the graph
2017 * Current the vertex label will just be vertex but
2018 * in the future the aai-node-type property will be replaced
2019 * by using the vertex label as when retrieving an vertex
2020 * and retrieving an single property on an vertex will pre-fetch
2021 * all the properties of that vertex and this is due to the following property
2024 * query.fast-property=true
2027 * JanusGraph doesn't provide the capability to override that
2028 * at a transaction level and there is a plan to move to vertex label
2029 * so it is best to utilize this for now and when the change is applied
2031 * @param vertices - list of vertices to delete from the graph
2033 void delete(List<Vertex> vertices) {
2034 StopWatch.conditionalStart();
2036 for (Vertex v : vertices) {
2037 LOGGER.debug("Removing vertex {} with label {}", v.id(), v.label());
2038 if (isDeltaEventsEnabled) {
2039 deltaForVertexDelete(v);
2041 // add the cousin vertexes of v to have their resource-version updated and notified on.
2042 v.edges(Direction.BOTH).forEachRemaining(e -> {
2043 if (e.property(EdgeProperty.CONTAINS.toString()).isPresent()
2044 && AAIDirection.NONE.toString().equals(e.<String>value(EdgeProperty.CONTAINS.toString()))) {
2045 e.bothVertices().forEachRemaining(cousinV -> {
2046 if (!v.equals(cousinV)) {
2047 edgeVertexes.add(cousinV);
2053 // if somewhere along the way v was added to the sets tracking the what is to be updated/notified on
2054 // it should be removed from them as v is to be deleted
2055 edgeVertexes.remove(v);
2056 updatedVertexes.remove(v);
2060 dbTimeMsecs += StopWatch.stopIfStarted();
2063 private void deltaForVertexDelete(Vertex vertex) {
2064 String aaiUri = vertex.property(AAIProperties.AAI_URI).value().toString();
2065 vertex.keys().forEach(k -> {
2066 List<Object> list = new ArrayList<>();
2067 vertex.properties(k).forEachRemaining(vp -> list.add(vp.value()));
2068 if (list.size() == 1) {
2069 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list.get(0)),
2070 DeltaAction.DELETE);
2072 addPropDelta(aaiUri, k, PropertyDeltaFactory.getDelta(DeltaAction.DELETE, list), DeltaAction.DELETE);
2076 vertex.edges(Direction.BOTH)
2077 .forEachRemaining(e -> deltaForEdge(aaiUri, e, DeltaAction.DELETE, DeltaAction.DELETE));
2084 * @param resourceVersion the resource version
2085 * @throws IllegalArgumentException the illegal argument exception
2086 * @throws AAIException the AAI exception
2087 * @throws InterruptedException the interrupted exception
2089 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion)
2090 throws IllegalArgumentException, AAIException {
2092 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
2094 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
2095 * These are far-fewer than seeing a prevent-delete on the vertex to be deleted
2096 * So its better to make these in 2 steps
2098 if (result && !deletableVertices.isEmpty()) {
2099 result = verifyPreventDeleteSemantics(deletableVertices);
2104 deleteWithTraversal(v);
2105 } catch (IllegalStateException e) {
2106 throw new AAIException("AAI_6110", e);
2115 * @param vertex the vertex
2116 * @param resourceVersion the resource version
2117 * @throws IllegalArgumentException the illegal argument exception
2118 * @throws AAIException the AAI exception
2119 * @throws InterruptedException the interrupted exception
2121 public void delete(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2122 throws IllegalArgumentException, AAIException {
2124 boolean result = verifyDeleteSemantics(vertex, resourceVersion, enableResourceVersion);
2129 deleteWithTraversal(vertex);
2130 } catch (IllegalStateException e) {
2131 throw new AAIException("AAI_6110", e);
2138 * Verify delete semantics.
2140 * @param vertex the vertex
2141 * @param resourceVersion the resource version
2142 * @return true, if successful
2143 * @throws AAIException the AAI exception
2145 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion)
2146 throws AAIException {
2149 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2150 if (enableResourceVersion) {
2151 this.verifyResourceVersion("delete", nodeType,
2152 vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType);
2154 List<Vertex> vertices = new ArrayList<>();
2155 vertices.add(vertex);
2156 result = verifyPreventDeleteSemantics(vertices);
2162 * Verify Prevent delete semantics.
2164 * @param vertices the list of vertices
2165 * @return true, if successful
2166 * @throws AAIException the AAI exception
2168 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
2169 boolean result = true;
2170 String errorDetail = " unknown delete semantic found";
2171 String aaiExceptionCode = "";
2173 StopWatch.conditionalStart();
2175 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a
2176 * "prevent-delete" condition
2177 * If yes - that should prevent the deletion of the vertex
2178 * Dedup makes sure we dont capture the prevent-delete vertices twice
2179 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
2182 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices)
2183 .union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV()
2184 .values(AAIProperties.NODE_TYPE),
2185 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV()
2186 .values(AAIProperties.NODE_TYPE))
2189 dbTimeMsecs += StopWatch.stopIfStarted();
2190 if (!preventDeleteVertices.isEmpty()) {
2191 aaiExceptionCode = "AAI_6110";
2193 String.format("Object is being reference by additional objects preventing it from being deleted."
2194 + " Please clean up references from the following types %s", preventDeleteVertices);
2198 throw new AAIException(aaiExceptionCode, errorDetail);
2204 * Verify resource version.
2206 * @param action the action
2207 * @param nodeType the node type
2208 * @param currentResourceVersion the current resource version
2209 * @param resourceVersion the resource version
2210 * @param uri the uri
2211 * @return true, if successful
2212 * @throws AAIException the AAI exception
2214 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion,
2215 String resourceVersion, String uri) throws AAIException {
2216 String enabled = "";
2217 String errorDetail = "";
2218 String aaiExceptionCode = "";
2219 boolean isDeleteResourceVersionOk = true;
2220 if (currentResourceVersion == null) {
2221 currentResourceVersion = "";
2224 if (resourceVersion == null) {
2225 resourceVersion = "";
2228 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
2230 } catch (AAIException e) {
2231 ErrorLogHelper.logException(e);
2233 if (enabled.equals("true")) {
2234 if ("delete".equals(action)) {
2235 isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion);
2237 if ((!isDeleteResourceVersionOk)
2238 || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) {
2239 if ("create".equals(action) && !resourceVersion.equals("")) {
2240 errorDetail = "resource-version passed for " + action + " of " + uri;
2241 aaiExceptionCode = "AAI_6135";
2242 } else if (resourceVersion.equals("")) {
2243 errorDetail = "resource-version not passed for " + action + " of " + uri;
2244 aaiExceptionCode = "AAI_6130";
2246 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
2247 aaiExceptionCode = "AAI_6131";
2250 throw new AAIException(aaiExceptionCode, errorDetail);
2258 * Verify resource version for delete.
2260 * @param currentResourceVersion the current resource version
2261 * @param resourceVersion the resource version
2262 * @return true, if successful or false if there is a mismatch
2264 private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) {
2266 boolean isDeleteResourceVersionOk = true;
2267 String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID,
2268 AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT);
2270 if ((!currentResourceVersion.equals(resourceVersion))
2271 && (!resourceVersion.equals(resourceVersionDisabledUuid))) {
2272 isDeleteResourceVersionOk = false;
2274 return isDeleteResourceVersionOk;
2278 * Convert from camel case.
2280 * @param name the name
2281 * @return the string
2283 private String convertFromCamelCase(String name) {
2285 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
2287 NamingExceptions exceptions = NamingExceptions.getInstance();
2288 result = exceptions.getDBName(result);
2293 private boolean canModify(Introspector obj, String propName, String requestContext) {
2294 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
2295 if (readOnly != null) {
2296 final String[] items = readOnly.split(",");
2297 for (String item : items) {
2298 if (requestContext.equals(item)) {
2306 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
2308 SideEffectRunner.Builder runnerBuilder = new SideEffectRunner.Builder(this.engine, this)
2309 .addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class);
2310 if (isMultiTenancyEnabled) {
2311 runnerBuilder.addSideEffect(OwnerCheck.class);
2313 runnerBuilder.build().execute(obj, self);
2316 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
2318 SideEffectRunner runner =
2319 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
2321 runner.execute(obj, self);
2324 private void enrichData(Introspector obj, Vertex self) throws AAIException {
2326 SideEffectRunner.Builder runnerBuilder =
2327 new SideEffectRunner.Builder(this.engine, this).addSideEffect(DataLinkReader.class);
2329 if (isMultiTenancyEnabled) {
2330 runnerBuilder.addSideEffect(OwnerCheck.class);
2332 runnerBuilder.build().execute(obj, self);
2335 public double getDBTimeMsecs() {
2336 return (dbTimeMsecs);
2340 * Db to object With Filters
2341 * This is for a one-time run with Tenant Isloation to only filter relationships
2343 * @param obj the obj
2344 * @param vertex the vertex from the graph
2345 * @param depth the depth
2346 * @param nodeOnly specify if to exclude relationships or not
2347 * @param filterCousinNodes
2348 * @return the introspector
2349 * @throws AAIException the AAI exception
2350 * @throws IllegalAccessException the illegal access exception
2351 * @throws IllegalArgumentException the illegal argument exception
2352 * @throws InvocationTargetException the invocation target exception
2353 * @throws SecurityException the security exception
2354 * @throws InstantiationException the instantiation exception
2355 * @throws NoSuchMethodException the no such method exception
2356 * @throws UnsupportedEncodingException the unsupported encoding exception
2357 * @throws MalformedURLException the malformed URL exception
2358 * @throws AAIUnknownObjectException
2359 * @throws URISyntaxException
2361 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertex, Set<Vertex> seen, int depth,
2362 boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes)
2363 throws AAIException, UnsupportedEncodingException {
2364 return dbToObjectWithFilters(obj, vertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes, false);
2368 * Db to object With Filters
2369 * This is for a one-time run with Tenant Isloation to only filter relationships
2370 * TODO: Chnage the original dbToObject to take filter parent/cousins
2372 * @param obj the obj
2373 * @param vertexParam the vertex from the graph
2374 * @param depth the depth
2375 * @param nodeOnly specify if to exclude relationships or not
2376 * @param filterCousinNodes
2377 * @param isSkipRelatedTo determine to incorporated related-to-property data
2378 * @return the introspector
2379 * @throws AAIException the AAI exception
2380 * @throws IllegalAccessException the illegal access exception
2381 * @throws IllegalArgumentException the illegal argument exception
2382 * @throws InvocationTargetException the invocation target exception
2383 * @throws SecurityException the security exception
2384 * @throws InstantiationException the instantiation exception
2385 * @throws NoSuchMethodException the no such method exception
2386 * @throws UnsupportedEncodingException the unsupported encoding exception
2387 * @throws MalformedURLException the malformed URL exception
2388 * @throws AAIUnknownObjectException
2389 * @throws URISyntaxException
2391 // TODO - See if you can merge the 2 dbToObjectWithFilters
2392 public Introspector dbToObjectWithFilters(Introspector obj, Vertex vertexParam, Set<Vertex> seen, int depth,
2393 boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes, boolean isSkipRelatedTo)
2394 throws AAIException, UnsupportedEncodingException {
2395 String cleanUp = FALSE;
2400 seen.add(vertexParam);
2401 boolean modified = false;
2402 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
2403 List<Object> getList = null;
2405 if (!(obj.isComplexType(property) || obj.isListType(property))) {
2406 this.copySimpleProperty(property, obj, vertexParam);
2409 if (obj.isComplexType(property)) {
2410 /* container case */
2412 if (!property.equals("relationship-list") && depth >= 0) {
2413 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
2414 Object result = dbToObjectWithFilters(argumentObject, vertexParam, seen, depth + 1, nodeOnly,
2415 filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2416 if (result != null) {
2417 obj.setValue(property, argumentObject.getUnderlyingObject());
2420 } else if (property.equals("relationship-list") && !nodeOnly) {
2421 /* relationships need to be handled correctly */
2422 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
2423 relationshipList = createFilteredRelationshipList(vertexParam, relationshipList, cleanUp,
2424 filterCousinNodes, isSkipRelatedTo);
2425 if (relationshipList != null) {
2426 obj.setValue(property, relationshipList.getUnderlyingObject());
2431 } else if (obj.isListType(property)) {
2433 if (property.equals("any")) {
2436 String genericType = obj.getGenericTypeClass(property).getSimpleName();
2437 if (obj.isComplexGenericType(property) && depth >= 0) {
2438 final String childDbName = convertFromCamelCase(genericType);
2439 String vertexType = vertexParam.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2442 boolean isThisParentRequired =
2443 filterParentNodes.parallelStream().anyMatch(childDbName::contains);
2445 EdgeRuleQuery query =
2446 new EdgeRuleQuery.Builder(vertexType, childDbName).edgeType(EdgeType.TREE).build();
2449 rule = edgeRules.getRule(query);
2450 } catch (EdgeRuleNotFoundException e) {
2451 throw new NoEdgeRuleFoundException(e);
2452 } catch (AmbiguousRuleChoiceException e) {
2453 throw new MultipleEdgeRuleFoundException(e);
2455 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isThisParentRequired) {
2456 Direction ruleDirection = rule.getDirection();
2457 List<Vertex> verticesList = new ArrayList<>();
2458 vertexParam.vertices(ruleDirection, rule.getLabel()).forEachRemaining(vertex -> {
2459 if (vertex.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName)) {
2460 verticesList.add(vertex);
2463 if (!verticesList.isEmpty()) {
2464 getList = obj.getValue(property);
2467 for (Vertex childVertex : verticesList) {
2468 if (!seen.contains(childVertex)) {
2469 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
2471 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth,
2472 nodeOnly, filterCousinNodes, filterParentNodes, isSkipRelatedTo);
2473 if (result != null && getList != null) {
2474 getList.add(argumentObject.getUnderlyingObject());
2479 LOGGER.warn("Cycle found while serializing vertex id={}",
2480 childVertex.id().toString());
2483 if (processed == 0) {
2484 // vertices were all seen, reset the list
2487 if (processed > 0) {
2491 } else if (obj.isSimpleGenericType(property)) {
2492 List<Object> temp = this.engine.getListProperty(vertexParam, property);
2494 getList = obj.getValue(property);
2495 getList.addAll(temp);
2506 // no changes were made to this obj, discard the instance
2510 this.enrichData(obj, vertexParam);
2516 * Creates the relationship list with the filtered node types.
2518 * @param vertex the vertex
2519 * @param obj the obj
2520 * @param cleanUp the clean up
2521 * @return the object
2522 * @throws IllegalArgumentException the illegal argument exception
2523 * @throws SecurityException the security exception
2524 * @throws UnsupportedEncodingException the unsupported encoding exception
2525 * @throws AAIException the AAI exception
2527 private Introspector createFilteredRelationshipList(Vertex vertex, Introspector obj, String cleanUp,
2528 List<String> filterNodes, boolean isSkipRelatedTo) throws UnsupportedEncodingException, AAIException {
2529 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(vertex);
2531 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
2532 String node = (String) item.property(AAIProperties.NODE_TYPE).orElse("");
2533 return filterNodes.parallelStream().anyMatch(node::contains);
2536 List<Object> relationshipObjList = obj.getValue(RELATIONSHIP);
2538 List<Vertex> cousins = new ArrayList<>();
2539 cousinVertices.forEachRemaining(cousins::add);
2540 for (Vertex cousin : cousins) {
2542 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty(RELATIONSHIP);
2543 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null, isSkipRelatedTo);
2544 if (result != null) {
2545 relationshipObjList.add(result);
2550 if (relationshipObjList.isEmpty()) {
2557 public Set<Vertex> touchStandardVertexPropertiesForEdges() {
2558 this.edgeVertexes.forEach(v -> this.touchStandardVertexProperties(v, false));
2559 return this.edgeVertexes;
2562 public void addVertexToEdgeVertexes(Vertex vertex) {
2563 this.edgeVertexes.add(vertex);
2566 private String urlToUri(String url) {
2567 if (url.startsWith("/")) {
2568 url = url.substring(1);
2571 if (url.endsWith("/")) {
2572 url = url.substring(0, url.length() - 1);
2575 // TODO - Check if this makes to do for model driven for base uri path
2576 url = url.replaceFirst("[a-z][a-z]*/v\\d+/", "");
2577 if (url.charAt(0) != '/') {