2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 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;
24 import java.io.UnsupportedEncodingException;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.InvocationTargetException;
27 import java.net.MalformedURLException;
29 import java.net.URISyntaxException;
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashSet;
34 import java.util.List;
36 import java.util.Optional;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Future;
43 import javax.ws.rs.core.UriBuilder;
45 import org.apache.commons.collections.IteratorUtils;
46 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
47 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
48 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
49 import org.apache.tinkerpop.gremlin.structure.Direction;
50 import org.apache.tinkerpop.gremlin.structure.Edge;
51 import org.apache.tinkerpop.gremlin.structure.Element;
52 import org.apache.tinkerpop.gremlin.structure.Vertex;
53 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
54 import org.javatuples.Pair;
55 import org.javatuples.Triplet;
56 import org.onap.aai.db.props.AAIProperties;
57 import org.onap.aai.exceptions.AAIException;
58 import org.onap.aai.introspection.Introspector;
59 import org.onap.aai.introspection.IntrospectorFactory;
60 import org.onap.aai.introspection.Loader;
61 import org.onap.aai.introspection.LoaderFactory;
62 import org.onap.aai.introspection.ModelType;
63 import org.onap.aai.introspection.PropertyPredicates;
64 import org.onap.aai.introspection.Version;
65 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
66 import org.onap.aai.introspection.sideeffect.DataCopy;
67 import org.onap.aai.introspection.sideeffect.DataLinkReader;
68 import org.onap.aai.introspection.sideeffect.DataLinkWriter;
69 import org.onap.aai.introspection.sideeffect.SideEffectRunner;
70 import org.onap.aai.logging.ErrorLogHelper;
71 import org.onap.aai.parsers.query.QueryParser;
72 import org.onap.aai.parsers.uri.URIParser;
73 import org.onap.aai.parsers.uri.URIToRelationshipObject;
74 import org.onap.aai.query.builder.QueryBuilder;
75 import org.onap.aai.schema.enums.ObjectMetadata;
76 import org.onap.aai.schema.enums.PropertyMetadata;
77 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
78 import org.onap.aai.serialization.db.util.VersionChecker;
79 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
80 import org.onap.aai.serialization.tinkerpop.TreeBackedVertex;
81 import org.onap.aai.util.AAIApiServerURLBase;
82 import org.onap.aai.util.AAIConfig;
83 import org.onap.aai.util.AAIConstants;
84 import org.onap.aai.workarounds.NamingExceptions;
86 import com.att.eelf.configuration.EELFLogger;
87 import com.att.eelf.configuration.EELFManager;
88 import com.google.common.base.CaseFormat;
89 import com.thinkaurelius.titan.core.SchemaViolationException;
91 public class DBSerializer {
93 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
95 private final TransactionalGraphEngine engine;
96 private final String sourceOfTruth;
97 private final ModelType introspectionType;
98 private final Version version;
99 private final Loader latestLoader;
100 private final EdgeRules edgeRules = EdgeRules.getInstance();
101 private final Loader loader;
102 private final String baseURL;
104 * Instantiates a new DB serializer.
106 * @param version the version
107 * @param engine the engine
109 * @param introspectionType the introspection type
110 * @param sourceOfTruth the source of truth
111 * @param llBuilder the ll builder
112 * @throws AAIException
114 public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
115 this.engine = engine;
116 this.sourceOfTruth = sourceOfTruth;
117 this.introspectionType = introspectionType;
118 this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST);
119 this.version = version;
120 this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version);
121 this.baseURL = AAIApiServerURLBase.get(version);
125 * Touch standard vertex properties.
128 * @param isNewVertex the is new vertex
130 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
132 String timeNowInSec = Long.toString(System.currentTimeMillis());
134 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
135 v.property(AAIProperties.CREATED_TS, timeNowInSec);
137 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
138 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
139 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
143 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
145 v.property(AAIProperties.NODE_TYPE, nodeType);
146 touchStandardVertexProperties(v, isNewVertex);
153 * Creates the new vertex.
155 * @param wrappedObject the wrapped object
157 * @throws UnsupportedEncodingException the unsupported encoding exception
158 * @throws AAIException the AAI exception
160 public Vertex createNewVertex(Introspector wrappedObject) {
163 Vertex v = this.engine.tx().addVertex();
164 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
172 * @param className the class name
176 * Removes the classpath from a class name
178 public String trimClassName (String className) {
179 String returnValue = "";
181 if (className.lastIndexOf('.') == -1) {
184 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
194 * @param uriQuery the uri query
195 * @param identifier the identifier
196 * @throws SecurityException the security exception
197 * @throws IllegalAccessException the illegal access exception
198 * @throws IllegalArgumentException the illegal argument exception
199 * @throws InvocationTargetException the invocation target exception
200 * @throws InstantiationException the instantiation exception
201 * @throws InterruptedException the interrupted exception
202 * @throws NoSuchMethodException the no such method exception
203 * @throws AAIException the AAI exception
204 * @throws UnsupportedEncodingException the unsupported encoding exception
205 * @throws AAIUnknownObjectException
207 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
210 if (uriQuery.isDependent()) {
211 //try to find the parent
212 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
213 if (!vertices.isEmpty()) {
214 Vertex parent = vertices.get(0);
215 this.reflectDependentVertex(parent, v, obj, requestContext);
217 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
220 serializeSingleVertex(v, obj, requestContext);
223 } catch (SchemaViolationException e) {
224 throw new AAIException("AAI_6117", e);
229 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
231 boolean isTopLevel = obj.isTopLevel();
233 v.property(AAIProperties.AAI_URI, obj.getURI());
235 processObject(obj, v, requestContext);
237 URI uri = this.getURIForVertex(v);
238 URIParser parser = new URIParser(this.loader, uri);
239 if (parser.validate()) {
240 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
241 if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
242 v.property(AAIProperties.AAI_URI, uri.toString());
246 } catch (SchemaViolationException e) {
247 throw new AAIException("AAI_6117", e);
254 * @param <T> the generic type
258 * @throws IllegalAccessException the illegal access exception
259 * @throws IllegalArgumentException the illegal argument exception
260 * @throws InvocationTargetException the invocation target exception
261 * @throws InstantiationException the instantiation exception
262 * @throws NoSuchMethodException the no such method exception
263 * @throws SecurityException the security exception
264 * @throws AAIException the AAI exception
265 * @throws UnsupportedEncodingException the unsupported encoding exception
266 * @throws AAIUnknownObjectException
269 * Helper method for reflectToDb
270 * Handles all the property setting
272 private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
273 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
274 properties.remove(AAIProperties.RESOURCE_VERSION);
275 List<Vertex> dependentVertexes = new ArrayList<>();
276 List<Vertex> processedVertexes = new ArrayList<>();
277 boolean isComplexType = false;
278 boolean isListType = false;
279 if (!obj.isContainer()) {
280 this.touchStandardVertexProperties(obj.getDbName(), v, false);
282 this.executePreSideEffects(obj, v);
283 for (String property : properties) {
285 final String propertyType;
286 propertyType = obj.getType(property);
287 isComplexType = obj.isComplexType(property);
288 isListType = obj.isListType(property);
289 value = obj.getValue(property);
291 if (!(isComplexType || isListType)) {
292 boolean canModify = this.canModify(obj, property, requestContext);
295 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
296 String dbProperty = property;
297 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
298 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
300 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
301 //data linked properties are ephemeral
302 //they are populated dynamically on GETs
306 if (!value.equals(v.property(dbProperty).orElse(null))) {
307 if (propertyType.toLowerCase().contains(".long")) {
308 v.property(dbProperty, new Integer(((Long)value).toString()));
310 v.property(dbProperty, value);
314 v.property(dbProperty).remove();
317 } else if (isListType) {
318 List<Object> list = (List<Object>)value;
319 if (obj.isComplexGenericType(property)) {
321 for (Object o : list) {
322 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
323 child.setURIChain(obj.getURI());
324 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
329 engine.setListProperty(v, property, list);
332 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
333 if (value != null) { //effectively ignore complex properties not included in the object we're processing
334 if (value.getClass().isArray()) {
336 int length = Array.getLength(value);
337 for (int i = 0; i < length; i ++) {
338 Object arrayElement = Array.get(value, i);
339 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
340 child.setURIChain(obj.getURI());
341 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
344 } else if (!property.equals("relationship-list")) {
346 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
347 if (introspector.isContainer()) {
348 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
349 introspector.setURIChain(obj.getURI());
351 processedVertexes.addAll(processObject(introspector, v, requestContext));
354 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
355 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
358 } else if (property.equals("relationship-list")) {
359 handleRelationships(obj, v);
364 this.writeThroughDefaults(v, obj);
365 /* handle those vertexes not touched */
366 for (Vertex toBeRemoved : processedVertexes) {
367 dependentVertexes.remove(toBeRemoved);
369 this.deleteItemsWithTraversal(dependentVertexes);
371 this.executePostSideEffects(obj, v);
372 return processedVertexes;
376 * Handle relationships.
379 * @param vertex the vertex
380 * @throws SecurityException the security exception
381 * @throws IllegalAccessException the illegal access exception
382 * @throws IllegalArgumentException the illegal argument exception
383 * @throws InvocationTargetException the invocation target exception
384 * @throws UnsupportedEncodingException the unsupported encoding exception
385 * @throws AAIException the AAI exception
388 * Handles the explicit relationships defined for an obj
390 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
394 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
395 processRelationshipList(wrappedRl, vertex);
402 * Process relationship list.
404 * @param wrapped the wrapped
406 * @throws UnsupportedEncodingException the unsupported encoding exception
407 * @throws AAIException the AAI exception
409 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
411 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
413 List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
414 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
416 for (Object relationship : relationships) {
418 Vertex cousinVertex = null;
420 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
421 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
423 if (wrappedRel.hasProperty("relationship-label")) {
424 label = wrappedRel.getValue("relationship-label");
427 List<Vertex> results = parser.getQueryBuilder().toList();
428 if (results.isEmpty()) {
429 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
430 ex.getTemplateVars().add(parser.getResultType());
431 ex.getTemplateVars().add(parser.getUri().toString());
434 //still an issue if there's more than one
435 cousinVertex = results.get(0);
438 if (cousinVertex != null) {
440 if (!edgeRules.hasEdgeRule(v, cousinVertex, label)) {
441 throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
442 + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"") +".");
443 } else if (edgeRules.hasTreeEdgeRule(v, cousinVertex) && !edgeRules.hasCousinEdgeRule(v, cousinVertex, label)) {
444 throw new AAIException("AAI_6145");
447 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
450 addEdges.add(new Triplet<>(v, cousinVertex, label));
452 existingEdges.remove(e);
457 for (Edge edge : existingEdges) {
460 for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
462 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
463 } catch (NoEdgeRuleFoundException e) {
464 throw new AAIException("AAI_6129", e);
471 * Write through defaults.
475 * @throws AAIUnknownObjectException
477 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
478 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
479 if (latest != null) {
480 Set<String> required = latest.getRequiredProperties();
482 for (String field : required) {
483 String defaultValue = null;
484 Object vertexProp = null;
485 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
486 if (defaultValue != null) {
487 vertexProp = v.<Object>property(field).orElse(null);
488 if (vertexProp == null) {
489 v.property(field, defaultValue);
499 * Reflect dependent vertex.
502 * @param dependentObj the dependent obj
504 * @throws IllegalAccessException the illegal access exception
505 * @throws IllegalArgumentException the illegal argument exception
506 * @throws InvocationTargetException the invocation target exception
507 * @throws InstantiationException the instantiation exception
508 * @throws NoSuchMethodException the no such method exception
509 * @throws SecurityException the security exception
510 * @throws AAIException the AAI exception
511 * @throws UnsupportedEncodingException the unsupported encoding exception
512 * @throws AAIUnknownObjectException
514 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
516 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
517 //List<Vertex> items = p.getQuery().toList();
518 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
519 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
520 query.createKeyQuery(dependentObj);
522 List<Vertex> items = query.toList();
524 Vertex dependentVertex = null;
525 if (items.size() == 1) {
526 dependentVertex = items.get(0);
527 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
529 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
530 dependentVertex = createNewVertex(dependentObj);
533 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
538 * Reflect dependent vertex.
540 * @param parent the parent
541 * @param child the child
544 * @throws IllegalAccessException the illegal access exception
545 * @throws IllegalArgumentException the illegal argument exception
546 * @throws InvocationTargetException the invocation target exception
547 * @throws InstantiationException the instantiation exception
548 * @throws NoSuchMethodException the no such method exception
549 * @throws SecurityException the security exception
550 * @throws AAIException the AAI exception
551 * @throws UnsupportedEncodingException the unsupported encoding exception
552 * @throws AAIUnknownObjectException
554 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
556 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
557 if (parentUri != null) {
558 String uri = obj.getURI();
559 child.property(AAIProperties.AAI_URI, parentUri + uri);
561 processObject(obj, child, requestContext);
564 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
566 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
567 if (canBeLinked != null && canBeLinked.equals("true")) {
568 boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
570 child.property(AAIProperties.LINKED, true);
573 edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
582 * @param vertices the vertices
584 * @param depth the depth
585 * @param cleanUp the clean up
586 * @return the introspector
587 * @throws AAIException the AAI exception
588 * @throws IllegalAccessException the illegal access exception
589 * @throws IllegalArgumentException the illegal argument exception
590 * @throws InvocationTargetException the invocation target exception
591 * @throws SecurityException the security exception
592 * @throws InstantiationException the instantiation exception
593 * @throws NoSuchMethodException the no such method exception
594 * @throws UnsupportedEncodingException the unsupported encoding exception
595 * @throws MalformedURLException the malformed URL exception
596 * @throws AAIUnknownObjectException
597 * @throws URISyntaxException
599 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
601 final int internalDepth;
602 if (depth == Integer.MAX_VALUE) {
603 internalDepth = depth--;
605 internalDepth = depth;
607 if (vertices.size() > 1 && !obj.isContainer()) {
608 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
609 } else if (obj.isContainer()) {
611 String listProperty = null;
612 for (String property : obj.getProperties()) {
613 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
614 listProperty = property;
618 final String propertyName = listProperty;
619 getList = (List)obj.getValue(listProperty);
621 /* This is an experimental multithreading experiment
624 ExecutorService pool = GetAllPool.getInstance().getPool();
626 List<Future<Object>> futures = new ArrayList<>();
627 for (Vertex v : vertices) {
628 Callable<Object> task = () -> {
629 Set<Vertex> seen = new HashSet<>();
630 Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
631 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
632 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
633 dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
634 return childObject.getUnderlyingObject();
635 //getList.add(childObject.getUnderlyingObject());
637 futures.add(pool.submit(task));
640 for (Future<Object> future : futures) {
642 getList.add(future.get());
643 } catch (ExecutionException | InterruptedException e) {
644 throw new AAIException("AAI_4000", e);
647 } else if (vertices.size() == 1) {
648 Set<Vertex> seen = new HashSet<>();
649 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
650 TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
651 dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
665 * @param seen the seen
666 * @param depth the depth
667 * @param cleanUp the clean up
668 * @return the introspector
669 * @throws IllegalAccessException the illegal access exception
670 * @throws IllegalArgumentException the illegal argument exception
671 * @throws InvocationTargetException the invocation target exception
672 * @throws SecurityException the security exception
673 * @throws InstantiationException the instantiation exception
674 * @throws NoSuchMethodException the no such method exception
675 * @throws UnsupportedEncodingException the unsupported encoding exception
676 * @throws AAIException the AAI exception
677 * @throws MalformedURLException the malformed URL exception
678 * @throws AAIUnknownObjectException
679 * @throws URISyntaxException
681 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
689 boolean modified = false;
690 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
691 List<Object> getList = null;
692 Vertex[] vertices = null;
694 if (!(obj.isComplexType(property) || obj.isListType(property))) {
695 this.copySimpleProperty(property, obj, v);
698 if (obj.isComplexType(property)) {
701 if (!property.equals("relationship-list") && depth >= 0) {
702 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
703 Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
704 if (result != null) {
705 obj.setValue(property, argumentObject.getUnderlyingObject());
708 } else if (property.equals("relationship-list") && !nodeOnly){
709 /* relationships need to be handled correctly */
710 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
711 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
712 if (relationshipList != null) {
714 obj.setValue(property, relationshipList.getUnderlyingObject());
719 } else if (obj.isListType(property)) {
721 if (property.equals("any")) {
724 String genericType = obj.getGenericTypeClass(property).getSimpleName();
725 if (obj.isComplexGenericType(property) && depth >= 0) {
726 final String childDbName = convertFromCamelCase(genericType);
727 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
730 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
731 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
732 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
733 Direction ruleDirection = rule.getDirection();
734 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
735 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
736 itr = verticesList.stream().filter(item -> {
737 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
740 getList = (List<Object>)obj.getValue(property);
744 while (itr.hasNext()) {
745 Vertex childVertex = itr.next();
746 if (!seen.contains(childVertex)) {
747 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
749 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
750 if (result != null) {
751 getList.add(argumentObject.getUnderlyingObject());
757 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
760 if (processed == 0) {
761 //vertices were all seen, reset the list
768 } else if (obj.isSimpleGenericType(property)) {
769 List<Object> temp = this.engine.getListProperty(v, property);
771 getList = (List<Object>)obj.getValue(property);
772 getList.addAll(temp);
783 //no changes were made to this obj, discard the instance
787 this.enrichData(obj, v);
793 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
794 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
795 if (nodeType == null) {
796 throw new AAIException("AAI_6143");
798 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
799 Set<Vertex> seen = new HashSet<>();
801 String cleanUp = "false";
802 boolean nodeOnly = true;
803 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
808 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
809 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
810 if (nodeType == null) {
811 throw new AAIException("AAI_6143");
813 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
814 Set<Vertex> seen = new HashSet<>();
815 int depth = AAIProperties.MAXIMUM_DEPTH;
816 String cleanUp = "false";
817 boolean nodeOnly = false;
818 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
819 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
820 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
825 * Copy simple property.
827 * @param property the property
830 * @throws InstantiationException the instantiation exception
831 * @throws IllegalAccessException the illegal access exception
832 * @throws IllegalArgumentException the illegal argument exception
833 * @throws InvocationTargetException the invocation target exception
834 * @throws NoSuchMethodException the no such method exception
835 * @throws SecurityException the security exception
837 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
838 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
839 String dbPropertyName = property;
840 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
841 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
843 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
846 obj.setValue(property, temp);
851 * Simple db to object.
855 * @throws InstantiationException the instantiation exception
856 * @throws IllegalAccessException the illegal access exception
857 * @throws IllegalArgumentException the illegal argument exception
858 * @throws InvocationTargetException the invocation target exception
859 * @throws NoSuchMethodException the no such method exception
860 * @throws SecurityException the security exception
862 private void simpleDbToObject (Introspector obj, Vertex v) {
863 for (String property : obj.getProperties()) {
866 if (!(obj.isComplexType(property) || obj.isListType(property))) {
867 this.copySimpleProperty(property, obj, v);
873 * Creates the relationship list.
877 * @param cleanUp the clean up
879 * @throws InstantiationException the instantiation exception
880 * @throws IllegalAccessException the illegal access exception
881 * @throws IllegalArgumentException the illegal argument exception
882 * @throws InvocationTargetException the invocation target exception
883 * @throws NoSuchMethodException the no such method exception
884 * @throws SecurityException the security exception
885 * @throws UnsupportedEncodingException the unsupported encoding exception
886 * @throws AAIException the AAI exception
887 * @throws MalformedURLException the malformed URL exception
888 * @throws URISyntaxException
890 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
892 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
894 List<Object> relationshipObjList = obj.getValue("relationship");
896 for (Vertex cousin : cousins) {
897 if (VersionChecker.apiVersionNeedsEdgeLabel(obj.getVersion())) {
898 List<Edge> edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin);
899 for (Edge e : edges) {
900 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
901 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e);
902 if (result != null) {
903 relationshipObjList.add(result);
907 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
908 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
909 if (result != null) {
910 relationshipObjList.add(result);
916 if (relationshipObjList.isEmpty()) {
924 * Process edge relationship.
926 * @param relationshipObj the relationship obj
927 * @param edge the edge
928 * @param direction the direction
929 * @param cleanUp the clean up
931 * @throws InstantiationException the instantiation exception
932 * @throws IllegalAccessException the illegal access exception
933 * @throws IllegalArgumentException the illegal argument exception
934 * @throws InvocationTargetException the invocation target exception
935 * @throws NoSuchMethodException the no such method exception
936 * @throws SecurityException the security exception
937 * @throws UnsupportedEncodingException the unsupported encoding exception
938 * @throws AAIException the AAI exception
939 * @throws MalformedURLException the malformed URL exception
940 * @throws AAIUnknownObjectException
941 * @throws URISyntaxException
943 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException {
946 //we must look up all parents in this case because we need to compute name-properties
947 //we cannot used the cached aaiUri to perform this action currently
948 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
949 //damaged vertex found, ignore
950 if (!tuple.isPresent()) {
953 List<Introspector> list = tuple.get().getValue1();
954 URI uri = this.getURIFromList(list);
956 URIToRelationshipObject uriParser = null;
957 Introspector result = null;
959 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
960 result = uriParser.getResult();
961 } catch (AAIException | URISyntaxException e) {
962 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " + e.getMessage(), e);
963 if ("true".equals(cleanUp)) {
964 this.deleteWithTraversal(tuple.get().getValue0());
968 if (!list.isEmpty()) {
969 this.addRelatedToProperty(result, list.get(0));
972 if (edge != null && result.hasProperty("relationship-label")) {
973 result.setValue("relationship-label", edge.label());
976 return result.getUnderlyingObject();
980 * Gets the URI for vertex.
983 * @return the URI for vertex
984 * @throws InstantiationException the instantiation exception
985 * @throws IllegalAccessException the illegal access exception
986 * @throws IllegalArgumentException the illegal argument exception
987 * @throws InvocationTargetException the invocation target exception
988 * @throws NoSuchMethodException the no such method exception
989 * @throws SecurityException the security exception
990 * @throws UnsupportedEncodingException the unsupported encoding exception
991 * @throws AAIUnknownObjectException
993 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
995 return getURIForVertex(v, false);
998 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
999 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1001 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1003 if (aaiUri != null && !overwrite) {
1004 uri = UriBuilder.fromPath(aaiUri).build();
1006 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false);
1007 if (tuple.isPresent()) {
1008 List<Introspector> list = tuple.get().getValue1();
1009 uri = this.getURIFromList(list);
1015 * Gets the URI from list.
1017 * @param list the list
1018 * @return the URI from list
1019 * @throws UnsupportedEncodingException the unsupported encoding exception
1021 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1023 StringBuilder sb = new StringBuilder();
1024 for (Introspector i : list) {
1025 sb.insert(0, i.getURI());
1028 uri = sb.toString();
1029 return UriBuilder.fromPath(uri).build();
1035 * @param start the start
1036 * @param removeDamaged the remove damaged
1037 * @return the parents
1038 * @throws InstantiationException the instantiation exception
1039 * @throws IllegalAccessException the illegal access exception
1040 * @throws IllegalArgumentException the illegal argument exception
1041 * @throws InvocationTargetException the invocation target exception
1042 * @throws NoSuchMethodException the no such method exception
1043 * @throws SecurityException the security exception
1044 * @throws AAIUnknownObjectException
1046 private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1048 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1049 List<Introspector> objs = new ArrayList<>();
1050 boolean shortCircuit = false;
1051 for (Vertex v : results) {
1052 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1053 Introspector obj = null;
1054 //vertex on the other end of this edge is bad
1055 if (nodeType == null) {
1056 //log something here about what was found and that it was removed
1057 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1058 if (removeDamaged) {
1059 this.deleteWithTraversal(v);
1061 shortCircuit = true;
1064 obj = loader.introspectorFromName(nodeType);
1065 } catch (AAIUnknownObjectException e) {
1066 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1072 //can't make a valid path because we don't understand this object
1075 this.simpleDbToObject(obj, v);
1080 //stop processing and don't return anything for this bad vertex
1082 return Optional.empty();
1085 return Optional.of(new Pair<>(results.get(results.size()-1), objs));
1088 * Takes a list of vertices and a list of objs and assumes they are in
1089 * the order you want the URIs to be nested.
1090 * [A,B,C] creates uris [A, AB, ABC]
1093 * @throws UnsupportedEncodingException
1094 * @throws URISyntaxException
1096 public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1098 StringBuilder uriChain = new StringBuilder();
1099 for (int i = 0; i < vertices.size(); i++) {
1102 v = vertices.get(i);
1103 aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1104 if (aaiUri != null) {
1105 uriChain.append(aaiUri);
1107 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1108 aaiUri = uri.toString();
1109 uriChain.append(aaiUri);
1110 v.property(AAIProperties.AAI_URI, uriChain.toString());
1121 * @throws AAIUnknownObjectException
1122 * @throws IllegalArgumentException elated to property.
1124 * @param relationship the relationship
1125 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1127 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1128 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1129 List<Introspector> relatedToProperties = new ArrayList<>();
1131 if (nameProps != null) {
1132 String[] props = nameProps.split(",");
1133 for (String prop : props) {
1134 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1135 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1136 relatedTo.setValue("property-value", child.getValue(prop));
1137 relatedToProperties.add(relatedTo);
1141 if (!relatedToProperties.isEmpty()) {
1142 List relatedToList = (List)relationship.getValue("related-to-property");
1143 for (Introspector obj : relatedToProperties) {
1144 relatedToList.add(obj.getUnderlyingObject());
1153 * @param relationship the relationship
1154 * @param inputVertex the input vertex
1155 * @return true, if successful
1156 * @throws UnsupportedEncodingException the unsupported encoding exception
1157 * @throws AAIException the AAI exception
1159 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1161 Vertex relatedVertex = null;
1163 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1165 String label = null;
1166 if (relationship.hasProperty("relationship-label")) {
1167 label = relationship.getValue("relationship-label");
1170 List<Vertex> results = parser.getQueryBuilder().toList();
1171 if (results.isEmpty()) {
1172 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1173 e.getTemplateVars().add(parser.getResultType());
1174 e.getTemplateVars().add(parser.getUri().toString());
1177 //still an issue if there's more than one
1178 relatedVertex = results.get(0);
1181 if (relatedVertex != null) {
1183 Edge e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1185 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1187 //attempted to link two vertexes already linked
1195 * Gets all the edges between of the type.
1197 * @param aVertex the out vertex
1198 * @param bVertex the in vertex
1199 * @return the edges between
1200 * @throws AAIException the AAI exception
1201 * @throws NoEdgeRuleFoundException
1203 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1205 List<Edge> result = new ArrayList<>();
1207 if (bVertex != null) {
1208 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1209 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1210 if (EdgeType.TREE.equals(type)) {
1211 findEdgesBetween = findEdgesBetween.not(__.has(EdgeProperty.CONTAINS.toString(), "NONE"));
1213 findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE");
1215 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1216 result = findEdgesBetween.toList();
1222 * Gets all the edges between the vertexes with the label and type.
1224 * @param aVertex the out vertex
1225 * @param bVertex the in vertex
1227 * @return the edges between
1228 * @throws AAIException the AAI exception
1230 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1232 List<Edge> result = new ArrayList<>();
1234 if (bVertex != null) {
1235 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex, label);
1236 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1237 for (Edge edge : edges) {
1238 if (edge.label().equals(rule.getLabel())) {
1248 * Gets the edge between with the label and edge type.
1250 * @param aVertex the out vertex
1251 * @param bVertex the in vertex
1253 * @return the edge between
1254 * @throws AAIException the AAI exception
1255 * @throws NoEdgeRuleFoundException
1257 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1259 if (bVertex != null) {
1261 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex, label);
1263 if (!edges.isEmpty()) {
1264 return edges.get(0);
1271 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1272 return this.getEdgeBetween(type, aVertex, bVertex, null);
1279 * @param relationship the relationship
1280 * @param inputVertex the input vertex
1281 * @return true, if successful
1282 * @throws UnsupportedEncodingException the unsupported encoding exception
1283 * @throws AAIException the AAI exception
1285 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1287 Vertex relatedVertex = null;
1289 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1291 List<Vertex> results = parser.getQueryBuilder().toList();
1293 String label = null;
1294 if (relationship.hasProperty("relationship-label")) {
1295 label = relationship.getValue("relationship-label");
1298 if (results.isEmpty()) {
1302 relatedVertex = results.get(0);
1305 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1306 } catch (NoEdgeRuleFoundException e) {
1307 throw new AAIException("AAI_6129", e);
1319 * Delete items with traversal.
1321 * @param vertexes the vertexes
1322 * @throws IllegalStateException the illegal state exception
1324 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1325 for (Vertex v : vertexes) {
1326 LOGGER.debug("About to delete the vertex with id: " + v.id());
1327 deleteWithTraversal(v);
1332 * Delete with traversal.
1334 * @param startVertex the start vertex
1336 public void deleteWithTraversal(Vertex startVertex) {
1338 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1340 for (Vertex v : results) {
1341 LOGGER.warn("Removing vertex " + v.id().toString());
1352 * @param resourceVersion the resource version
1353 * @throws IllegalArgumentException the illegal argument exception
1354 * @throws AAIException the AAI exception
1355 * @throws InterruptedException the interrupted exception
1357 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1359 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1362 deleteWithTraversal(v);
1363 } catch (IllegalStateException e) {
1364 throw new AAIException("AAI_6110", e);
1373 * Verify delete semantics.
1375 * @param vertex the vertex
1376 * @param resourceVersion the resource version
1377 * @return true, if successful
1378 * @throws AAIException the AAI exception
1380 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1381 boolean result = true;
1382 String nodeType = "";
1383 String errorDetail = " unknown delete semantic found";
1384 String aaiExceptionCode = "";
1385 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1386 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1388 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertex).union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)).dedup().toList();
1390 if (!preventDeleteVertices.isEmpty()) {
1391 aaiExceptionCode = "AAI_6110";
1392 errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices);
1396 throw new AAIException(aaiExceptionCode, errorDetail);
1402 * Verify resource version.
1404 * @param action the action
1405 * @param nodeType the node type
1406 * @param currentResourceVersion the current resource version
1407 * @param resourceVersion the resource version
1408 * @param uri the uri
1409 * @return true, if successful
1410 * @throws AAIException the AAI exception
1412 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1413 String enabled = "";
1414 String errorDetail = "";
1415 String aaiExceptionCode = "";
1416 if (currentResourceVersion == null) {
1417 currentResourceVersion = "";
1420 if (resourceVersion == null) {
1421 resourceVersion = "";
1424 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1425 } catch (AAIException e) {
1426 ErrorLogHelper.logException(e);
1428 // We're only doing the resource version checks for v5 and later
1429 if (enabled.equals("true")) {
1430 if (!currentResourceVersion.equals(resourceVersion)) {
1431 if (action.equals("create") && !resourceVersion.equals("")) {
1432 errorDetail = "resource-version passed for " + action + " of " + uri;
1433 aaiExceptionCode = "AAI_6135";
1434 } else if (resourceVersion.equals("")) {
1435 errorDetail = "resource-version not passed for " + action + " of " + uri;
1436 aaiExceptionCode = "AAI_6130";
1438 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1439 aaiExceptionCode = "AAI_6131";
1442 throw new AAIException(aaiExceptionCode, errorDetail);
1450 * Convert from camel case.
1452 * @param name the name
1453 * @return the string
1455 private String convertFromCamelCase (String name) {
1457 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1459 NamingExceptions exceptions = NamingExceptions.getInstance();
1460 result = exceptions.getDBName(result);
1465 private boolean canModify(Introspector obj, String propName, String requestContext) {
1466 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1467 if (readOnly != null) {
1468 final String[] items = readOnly.split(",");
1469 for (String item : items) {
1470 if (requestContext.equals(item)) {
1478 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1480 SideEffectRunner runner = new SideEffectRunner
1481 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1483 runner.execute(obj, self);
1486 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1488 SideEffectRunner runner = new SideEffectRunner
1489 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1491 runner.execute(obj, self);
1494 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1496 SideEffectRunner runner = new SideEffectRunner
1497 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1499 runner.execute(obj, self);