2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.onap.aai.serialization.db;
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.google.common.base.CaseFormat;
26 import org.janusgraph.core.SchemaViolationException;
27 import org.apache.commons.collections.IteratorUtils;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
30 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
31 import org.apache.tinkerpop.gremlin.structure.*;
32 import org.javatuples.Pair;
33 import org.javatuples.Triplet;
34 import org.onap.aai.db.props.AAIProperties;
35 import org.onap.aai.edges.EdgeIngestor;
36 import org.onap.aai.edges.EdgeRule;
37 import org.onap.aai.edges.EdgeRuleQuery;
38 import org.onap.aai.edges.enums.AAIDirection;
39 import org.onap.aai.edges.enums.EdgeField;
40 import org.onap.aai.edges.enums.EdgeProperty;
41 import org.onap.aai.edges.enums.EdgeType;
42 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
43 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
44 import org.onap.aai.exceptions.AAIException;
45 import org.onap.aai.introspection.*;
46 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
47 import org.onap.aai.introspection.sideeffect.*;
48 import org.onap.aai.logging.ErrorLogHelper;
49 import org.onap.aai.logging.LogFormatTools;
50 import org.onap.aai.logging.StopWatch;
51 import org.onap.aai.parsers.query.QueryParser;
52 import org.onap.aai.parsers.uri.URIParser;
53 import org.onap.aai.parsers.uri.URIToRelationshipObject;
54 import org.onap.aai.query.builder.QueryBuilder;
55 import org.onap.aai.schema.enums.ObjectMetadata;
56 import org.onap.aai.schema.enums.PropertyMetadata;
57 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
58 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
59 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
60 import org.onap.aai.serialization.tinkerpop.TreeBackedVertex;
61 import org.onap.aai.setup.SchemaVersion;
62 import org.onap.aai.setup.SchemaVersions;
63 import org.onap.aai.util.AAIConfig;
64 import org.onap.aai.util.AAIConstants;
65 import org.onap.aai.workarounds.NamingExceptions;
66 import org.springframework.context.ApplicationContext;
67 import org.onap.aai.concurrent.AaiCallable;
68 import org.onap.aai.config.SpringContextAware;
70 import javax.ws.rs.core.UriBuilder;
71 import java.io.UnsupportedEncodingException;
72 import java.lang.reflect.Array;
73 import java.lang.reflect.InvocationTargetException;
74 import java.net.MalformedURLException;
76 import java.net.URISyntaxException;
78 import java.util.concurrent.ExecutionException;
79 import java.util.concurrent.ExecutorService;
80 import java.util.concurrent.Future;
81 import java.util.regex.Matcher;
82 import java.util.regex.Pattern;
83 import org.onap.aai.serialization.engines.query.QueryEngine;
85 public class DBSerializer {
87 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
89 private final TransactionalGraphEngine engine;
90 private final String sourceOfTruth;
91 private final ModelType introspectionType;
92 private final SchemaVersion version;
93 private final Loader latestLoader;
94 private EdgeSerializer edgeSer;
95 private EdgeIngestor edgeRules;
96 private final Loader loader;
97 private final String baseURL;
98 private double dbTimeMsecs = 0;
100 private SchemaVersions schemaVersions;
102 * Instantiates a new DB serializer.
104 * @param version the version
105 * @param engine the engine
106 * @param introspectionType the introspection type
107 * @param sourceOfTruth the source of truth
108 * @throws AAIException
110 public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
111 this.engine = engine;
112 this.sourceOfTruth = sourceOfTruth;
113 this.introspectionType = introspectionType;
114 this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
115 SchemaVersion LATEST = schemaVersions.getDefaultVersion();
116 this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST);
117 this.version = version;
118 this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version);
119 this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE);
123 private void initBeans() {
124 //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
125 ApplicationContext ctx = SpringContextAware.getApplicationContext();
126 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
128 EdgeSerializer es = ctx.getBean(EdgeSerializer.class);
129 setEdgeSerializer(es);
132 private void backupESInit() {
133 setEdgeSerializer(new EdgeSerializer(this.edgeRules));
136 public void setEdgeSerializer(EdgeSerializer edgeSer) {
137 this.edgeSer = edgeSer;
140 public EdgeSerializer getEdgeSeriailizer() {
144 public void setEdgeIngestor(EdgeIngestor ei) {
148 public EdgeIngestor getEdgeIngestor(){
149 return this.edgeRules;
153 * Touch standard vertex properties.
156 * @param isNewVertex the is new vertex
158 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
160 String timeNowInSec = Long.toString(System.currentTimeMillis());
162 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
163 v.property(AAIProperties.CREATED_TS, timeNowInSec);
164 v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString());
166 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
167 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
168 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
172 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
174 v.property(AAIProperties.NODE_TYPE, nodeType);
175 touchStandardVertexProperties(v, isNewVertex);
182 * Creates the new vertex.
184 * @param wrappedObject the wrapped object
186 * @throws UnsupportedEncodingException the unsupported encoding exception
187 * @throws AAIException the AAI exception
189 public Vertex createNewVertex(Introspector wrappedObject) {
192 StopWatch.conditionalStart();
193 v = this.engine.tx().addVertex();
194 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
197 dbTimeMsecs += StopWatch.stopIfStarted();
205 * @param className the class name
209 * Removes the classpath from a class name
211 public String trimClassName (String className) {
212 String returnValue = "";
214 if (className.lastIndexOf('.') == -1) {
217 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
227 * @param uriQuery the uri query
228 * @param identifier the identifier
229 * @throws SecurityException the security exception
230 * @throws IllegalAccessException the illegal access exception
231 * @throws IllegalArgumentException the illegal argument exception
232 * @throws InvocationTargetException the invocation target exception
233 * @throws InstantiationException the instantiation exception
234 * @throws InterruptedException the interrupted exception
235 * @throws NoSuchMethodException the no such method exception
236 * @throws AAIException the AAI exception
237 * @throws UnsupportedEncodingException the unsupported encoding exception
238 * @throws AAIUnknownObjectException
240 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
241 StopWatch.conditionalStart();
243 if (uriQuery.isDependent()) {
244 //try to find the parent
245 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
246 if (!vertices.isEmpty()) {
247 Vertex parent = vertices.get(0);
248 this.reflectDependentVertex(parent, v, obj, requestContext);
250 dbTimeMsecs += StopWatch.stopIfStarted();
251 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
254 serializeSingleVertex(v, obj, requestContext);
257 } catch (SchemaViolationException e) {
258 dbTimeMsecs += StopWatch.stopIfStarted();
259 throw new AAIException("AAI_6117", e);
261 dbTimeMsecs += StopWatch.stopIfStarted();
264 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
265 StopWatch.conditionalStart();
267 boolean isTopLevel = obj.isTopLevel();
269 addUriIfNeeded(v, obj.getURI());
272 processObject(obj, v, requestContext);
274 URI uri = this.getURIForVertex(v);
275 URIParser parser = new URIParser(this.loader, uri);
276 if (parser.validate()) {
277 addUriIfNeeded(v, uri.toString());
280 } catch (SchemaViolationException e) {
281 throw new AAIException("AAI_6117", e);
284 dbTimeMsecs += StopWatch.stopIfStarted();
288 private void addUriIfNeeded(Vertex v, String uri) {
289 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
290 if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) {
291 v.property(AAIProperties.AAI_URI, uri);
298 * @param <T> the generic type
302 * @throws IllegalAccessException the illegal access exception
303 * @throws IllegalArgumentException the illegal argument exception
304 * @throws InvocationTargetException the invocation target exception
305 * @throws InstantiationException the instantiation exception
306 * @throws NoSuchMethodException the no such method exception
307 * @throws SecurityException the security exception
308 * @throws AAIException the AAI exception
309 * @throws UnsupportedEncodingException the unsupported encoding exception
310 * @throws AAIUnknownObjectException
313 * Helper method for reflectToDb
314 * Handles all the property setting
316 private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
317 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
318 properties.remove(AAIProperties.RESOURCE_VERSION);
319 List<Vertex> dependentVertexes = new ArrayList<>();
320 List<Vertex> processedVertexes = new ArrayList<>();
321 boolean isComplexType = false;
322 boolean isListType = false;
323 if (!obj.isContainer()) {
324 this.touchStandardVertexProperties(obj.getDbName(), v, false);
326 this.executePreSideEffects(obj, v);
327 for (String property : properties) {
329 final String propertyType;
330 propertyType = obj.getType(property);
331 isComplexType = obj.isComplexType(property);
332 isListType = obj.isListType(property);
333 value = obj.getValue(property);
335 if (!(isComplexType || isListType)) {
336 boolean canModify = this.canModify(obj, property, requestContext);
339 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
340 String dbProperty = property;
341 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
342 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
344 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
345 //data linked properties are ephemeral
346 //they are populated dynamically on GETs
350 if (!value.equals(v.property(dbProperty).orElse(null))) {
351 if (propertyType.toLowerCase().contains(".long")) {
352 v.property(dbProperty, new Integer(((Long)value).toString()));
354 v.property(dbProperty, value);
358 v.property(dbProperty).remove();
361 } else if (isListType) {
362 List<Object> list = (List<Object>)value;
363 if (obj.isComplexGenericType(property)) {
365 for (Object o : list) {
366 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
367 child.setURIChain(obj.getURI());
368 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
373 engine.setListProperty(v, property, list);
376 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
377 if (value != null) { //effectively ignore complex properties not included in the object we're processing
378 if (value.getClass().isArray()) {
380 int length = Array.getLength(value);
381 for (int i = 0; i < length; i ++) {
382 Object arrayElement = Array.get(value, i);
383 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
384 child.setURIChain(obj.getURI());
385 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
388 } else if (!property.equals("relationship-list")) {
390 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
391 if (introspector.isContainer()) {
392 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
393 introspector.setURIChain(obj.getURI());
395 processedVertexes.addAll(processObject(introspector, v, requestContext));
398 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
399 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
402 } else if (property.equals("relationship-list")) {
403 handleRelationships(obj, v);
408 this.writeThroughDefaults(v, obj);
409 /* handle those vertexes not touched */
410 for (Vertex toBeRemoved : processedVertexes) {
411 dependentVertexes.remove(toBeRemoved);
413 this.deleteItemsWithTraversal(dependentVertexes);
415 this.executePostSideEffects(obj, v);
416 return processedVertexes;
420 * Handle relationships.
423 * @param vertex the vertex
424 * @throws SecurityException the security exception
425 * @throws IllegalAccessException the illegal access exception
426 * @throws IllegalArgumentException the illegal argument exception
427 * @throws InvocationTargetException the invocation target exception
428 * @throws UnsupportedEncodingException the unsupported encoding exception
429 * @throws AAIException the AAI exception
432 * Handles the explicit relationships defined for an obj
434 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
438 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
439 processRelationshipList(wrappedRl, vertex);
446 * Process relationship list.
448 * @param wrapped the wrapped
450 * @throws UnsupportedEncodingException the unsupported encoding exception
451 * @throws AAIException the AAI exception
453 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
455 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
457 List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>();
458 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
460 for (Object relationship : relationships) {
462 Vertex cousinVertex = null;
464 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
465 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
467 if (wrappedRel.hasProperty("relationship-label")) {
468 label = wrappedRel.getValue("relationship-label");
471 List<Vertex> results = parser.getQueryBuilder().toList();
472 if (results.isEmpty()) {
473 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
474 ex.getTemplateVars().add(parser.getResultType());
475 ex.getTemplateVars().add(parser.getUri().toString());
478 //still an issue if there's more than one
479 cousinVertex = results.get(0);
482 if (cousinVertex != null) {
483 String vType = (String)v.property(AAIProperties.NODE_TYPE).value();
484 String cousinType = (String)cousinVertex.property(AAIProperties.NODE_TYPE).value();
485 EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label);
488 if (!edgeRules.hasRule(baseQ.build())) {
489 throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
490 + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"") +".");
491 } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) {
492 throw new AAIException("AAI_6145");
495 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label);
498 addEdges.add(new Triplet<>(v, cousinVertex, label));
500 existingEdges.remove(e);
505 for (Edge edge : existingEdges) {
508 for (Triplet<Vertex, Vertex, String> triplet : addEdges) {
510 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2());
511 } catch (NoEdgeRuleFoundException e) {
512 throw new AAIException("AAI_6129", e);
519 * Write through defaults.
523 * @throws AAIUnknownObjectException
525 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
526 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
527 if (latest != null) {
528 Set<String> required = latest.getRequiredProperties();
530 for (String field : required) {
531 String defaultValue = null;
532 Object vertexProp = null;
533 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
534 if (defaultValue != null) {
535 vertexProp = v.<Object>property(field).orElse(null);
536 if (vertexProp == null) {
537 v.property(field, defaultValue);
547 * Reflect dependent vertex.
550 * @param dependentObj the dependent obj
552 * @throws IllegalAccessException the illegal access exception
553 * @throws IllegalArgumentException the illegal argument exception
554 * @throws InvocationTargetException the invocation target exception
555 * @throws InstantiationException the instantiation exception
556 * @throws NoSuchMethodException the no such method exception
557 * @throws SecurityException the security exception
558 * @throws AAIException the AAI exception
559 * @throws UnsupportedEncodingException the unsupported encoding exception
560 * @throws AAIUnknownObjectException
562 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
564 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
565 //List<Vertex> items = p.getQuery().toList();
566 QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v);
567 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
568 query.createKeyQuery(dependentObj);
570 List<Vertex> items = query.toList();
572 Vertex dependentVertex = null;
573 if (items.size() == 1) {
574 dependentVertex = items.get(0);
575 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
577 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
578 dependentVertex = createNewVertex(dependentObj);
581 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
586 * Reflect dependent vertex.
588 * @param parent the parent
589 * @param child the child
592 * @throws IllegalAccessException the illegal access exception
593 * @throws IllegalArgumentException the illegal argument exception
594 * @throws InvocationTargetException the invocation target exception
595 * @throws InstantiationException the instantiation exception
596 * @throws NoSuchMethodException the no such method exception
597 * @throws SecurityException the security exception
598 * @throws AAIException the AAI exception
599 * @throws UnsupportedEncodingException the unsupported encoding exception
600 * @throws AAIUnknownObjectException
602 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
604 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
605 if (parentUri != null) {
608 addUriIfNeeded(child, parentUri + uri);
610 processObject(obj, child, requestContext);
613 e = this.getEdgeBetween(EdgeType.TREE, parent, child, null);
615 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
616 if (canBeLinked != null && canBeLinked.equals("true")) {
617 Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext));
618 boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
620 child.property(AAIProperties.LINKED, true);
623 edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
629 private SchemaVersion getVerForContext(String requestContext) {
630 Pattern pattern = Pattern.compile("v[0-9]+");
631 Matcher m = pattern.matcher(requestContext);
635 return new SchemaVersion(requestContext);
642 * @param vertices the vertices
644 * @param depth the depth
645 * @param cleanUp the clean up
646 * @return the introspector
647 * @throws AAIException the AAI exception
648 * @throws IllegalAccessException the illegal access exception
649 * @throws IllegalArgumentException the illegal argument exception
650 * @throws InvocationTargetException the invocation target exception
651 * @throws SecurityException the security exception
652 * @throws InstantiationException the instantiation exception
653 * @throws NoSuchMethodException the no such method exception
654 * @throws UnsupportedEncodingException the unsupported encoding exception
655 * @throws MalformedURLException the malformed URL exception
656 * @throws AAIUnknownObjectException
657 * @throws URISyntaxException
659 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
660 final int internalDepth;
661 if (depth == Integer.MAX_VALUE) {
662 internalDepth = depth--;
664 internalDepth = depth;
666 StopWatch.conditionalStart();
667 if (vertices.size() > 1 && !obj.isContainer()) {
668 dbTimeMsecs += StopWatch.stopIfStarted();
669 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
670 } else if (obj.isContainer()) {
672 String listProperty = null;
673 for (String property : obj.getProperties()) {
674 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
675 listProperty = property;
679 final String propertyName = listProperty;
680 getList = (List)obj.getValue(listProperty);
682 /* This is an experimental multithreading experiment
685 ExecutorService pool = GetAllPool.getInstance().getPool();
687 List<Future<Object>> futures = new ArrayList<>();
689 QueryEngine tgEngine = this.engine.getQueryEngine();
690 for (Vertex v : vertices) {
692 AaiCallable<Object> task = new AaiCallable<Object>() {
694 public Object process() throws UnsupportedEncodingException, AAIException {
695 Set<Vertex> seen = new HashSet<>();
696 Introspector childObject;
698 childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
699 } catch (AAIUnknownObjectException e) {
702 Tree<Element> tree = tgEngine.findSubGraph(v, internalDepth, nodeOnly);
703 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
705 dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
706 } catch (UnsupportedEncodingException e) {
708 } catch (AAIException e) {
711 return childObject.getUnderlyingObject();
712 //getList.add(childObject.getUnderlyingObject());
715 futures.add(pool.submit(task));
718 for (Future<Object> future : futures) {
720 getList.add(future.get());
721 } catch (ExecutionException e) {
722 dbTimeMsecs += StopWatch.stopIfStarted();
723 throw new AAIException("AAI_4000", e);
724 } catch (InterruptedException e) {
725 dbTimeMsecs += StopWatch.stopIfStarted();
726 throw new AAIException("AAI_4000", e);
729 } else if (vertices.size() == 1) {
730 Set<Vertex> seen = new HashSet<>();
731 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
732 TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
733 dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
738 dbTimeMsecs += StopWatch.stopIfStarted();
747 * @param seen the seen
748 * @param depth the depth
749 * @param cleanUp the clean up
750 * @return the introspector
751 * @throws IllegalAccessException the illegal access exception
752 * @throws IllegalArgumentException the illegal argument exception
753 * @throws InvocationTargetException the invocation target exception
754 * @throws SecurityException the security exception
755 * @throws InstantiationException the instantiation exception
756 * @throws NoSuchMethodException the no such method exception
757 * @throws UnsupportedEncodingException the unsupported encoding exception
758 * @throws AAIException the AAI exception
759 * @throws MalformedURLException the malformed URL exception
760 * @throws AAIUnknownObjectException
761 * @throws URISyntaxException
763 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
771 boolean modified = false;
772 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
773 List<Object> getList = null;
774 Vertex[] vertices = null;
776 if (!(obj.isComplexType(property) || obj.isListType(property))) {
777 this.copySimpleProperty(property, obj, v);
780 if (obj.isComplexType(property)) {
783 if (!property.equals("relationship-list") && depth >= 0) {
784 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
785 Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
786 if (result != null) {
787 obj.setValue(property, argumentObject.getUnderlyingObject());
790 } else if (property.equals("relationship-list") && !nodeOnly){
791 /* relationships need to be handled correctly */
792 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
793 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
794 if (relationshipList != null) {
796 obj.setValue(property, relationshipList.getUnderlyingObject());
801 } else if (obj.isListType(property)) {
803 if (property.equals("any")) {
806 String genericType = obj.getGenericTypeClass(property).getSimpleName();
807 if (obj.isComplexGenericType(property) && depth >= 0) {
808 final String childDbName = convertFromCamelCase(genericType);
809 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
813 rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build());
814 } catch (EdgeRuleNotFoundException e) {
815 throw new NoEdgeRuleFoundException(e);
816 } catch (AmbiguousRuleChoiceException e) {
817 throw new MultipleEdgeRuleFoundException(e);
819 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
820 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
821 Direction ruleDirection = rule.getDirection();
822 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
823 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
824 itr = verticesList.stream().filter(item -> {
825 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
828 getList = (List<Object>)obj.getValue(property);
832 while (itr.hasNext()) {
833 Vertex childVertex = itr.next();
834 if (!seen.contains(childVertex)) {
835 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
837 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
838 if (result != null) {
839 getList.add(argumentObject.getUnderlyingObject());
845 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
848 if (processed == 0) {
849 //vertices were all seen, reset the list
856 } else if (obj.isSimpleGenericType(property)) {
857 List<Object> temp = this.engine.getListProperty(v, property);
859 getList = (List<Object>)obj.getValue(property);
860 getList.addAll(temp);
871 //no changes were made to this obj, discard the instance
875 this.enrichData(obj, v);
881 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
882 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
883 if (nodeType == null) {
884 throw new AAIException("AAI_6143");
887 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
888 Set<Vertex> seen = new HashSet<>();
890 String cleanUp = "false";
891 boolean nodeOnly = true;
892 StopWatch.conditionalStart();
893 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
894 dbTimeMsecs += StopWatch.stopIfStarted();
898 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
899 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
900 if (nodeType == null) {
901 throw new AAIException("AAI_6143");
903 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
904 Set<Vertex> seen = new HashSet<>();
905 int depth = AAIProperties.MAXIMUM_DEPTH;
906 String cleanUp = "false";
907 boolean nodeOnly = false;
908 StopWatch.conditionalStart();
909 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
910 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
911 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
912 dbTimeMsecs += StopWatch.stopIfStarted();
916 * Copy simple property.
918 * @param property the property
921 * @throws InstantiationException the instantiation exception
922 * @throws IllegalAccessException the illegal access exception
923 * @throws IllegalArgumentException the illegal argument exception
924 * @throws InvocationTargetException the invocation target exception
925 * @throws NoSuchMethodException the no such method exception
926 * @throws SecurityException the security exception
928 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
930 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
931 String dbPropertyName = property;
933 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
934 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
939 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
941 obj.setValue(property, temp);
946 * Simple db to object.
950 * @throws InstantiationException the instantiation exception
951 * @throws IllegalAccessException the illegal access exception
952 * @throws IllegalArgumentException the illegal argument exception
953 * @throws InvocationTargetException the invocation target exception
954 * @throws NoSuchMethodException the no such method exception
955 * @throws SecurityException the security exception
957 private void simpleDbToObject (Introspector obj, Vertex v) {
958 for (String property : obj.getProperties()) {
961 if (!(obj.isComplexType(property) || obj.isListType(property))) {
962 this.copySimpleProperty(property, obj, v);
968 * Creates the relationship list.
972 * @param cleanUp the clean up
974 * @throws InstantiationException the instantiation exception
975 * @throws IllegalAccessException the illegal access exception
976 * @throws IllegalArgumentException the illegal argument exception
977 * @throws InvocationTargetException the invocation target exception
978 * @throws NoSuchMethodException the no such method exception
979 * @throws SecurityException the security exception
980 * @throws UnsupportedEncodingException the unsupported encoding exception
981 * @throws AAIException the AAI exception
982 * @throws MalformedURLException the malformed URL exception
983 * @throws URISyntaxException
985 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
987 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
989 List<Object> relationshipObjList = obj.getValue("relationship");
991 for (Vertex cousin : cousins) {
992 if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) {
993 List<Edge> edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin);
994 for (Edge e : edges) {
995 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
996 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e);
997 if (result != null) {
998 relationshipObjList.add(result);
1002 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1003 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1004 if (result != null) {
1005 relationshipObjList.add(result);
1011 if (relationshipObjList.isEmpty()) {
1019 * Process edge relationship.
1021 * @param relationshipObj the relationship obj
1022 * @param edge the edge
1023 * @param cleanUp the clean up
1024 * @return the object
1025 * @throws InstantiationException the instantiation exception
1026 * @throws IllegalAccessException the illegal access exception
1027 * @throws IllegalArgumentException the illegal argument exception
1028 * @throws InvocationTargetException the invocation target exception
1029 * @throws NoSuchMethodException the no such method exception
1030 * @throws SecurityException the security exception
1031 * @throws UnsupportedEncodingException the unsupported encoding exception
1032 * @throws AAIException the AAI exception
1033 * @throws MalformedURLException the malformed URL exception
1034 * @throws AAIUnknownObjectException
1035 * @throws URISyntaxException
1037 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException {
1040 //we must look up all parents in this case because we need to compute name-properties
1041 //we cannot used the cached aaiUri to perform this action currently
1042 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
1043 //damaged vertex found, ignore
1044 if (!tuple.isPresent()) {
1047 List<Introspector> list = tuple.get().getValue1();
1048 URI uri = this.getURIFromList(list);
1050 URIToRelationshipObject uriParser = null;
1051 Introspector result = null;
1053 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
1054 result = uriParser.getResult();
1055 } catch (AAIException | URISyntaxException e) {
1056 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": "
1057 + e.getMessage() + " " + LogFormatTools.getStackTop(e));
1058 if ("true".equals(cleanUp)) {
1059 this.deleteWithTraversal(tuple.get().getValue0());
1063 if (!list.isEmpty()) {
1064 this.addRelatedToProperty(result, list.get(0));
1067 if (edge != null && result.hasProperty("relationship-label")) {
1068 result.setValue("relationship-label", edge.label());
1071 return result.getUnderlyingObject();
1075 * Gets the URI for vertex.
1078 * @return the URI for vertex
1079 * @throws InstantiationException the instantiation exception
1080 * @throws IllegalAccessException the illegal access exception
1081 * @throws IllegalArgumentException the illegal argument exception
1082 * @throws InvocationTargetException the invocation target exception
1083 * @throws NoSuchMethodException the no such method exception
1084 * @throws SecurityException the security exception
1085 * @throws UnsupportedEncodingException the unsupported encoding exception
1086 * @throws AAIUnknownObjectException
1088 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
1090 return getURIForVertex(v, false);
1093 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
1094 URI uri = UriBuilder.fromPath("/unknown-uri").build();
1096 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1098 if (aaiUri != null && !overwrite) {
1099 uri = UriBuilder.fromPath(aaiUri).build();
1101 StopWatch.conditionalStart();
1102 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false);
1103 dbTimeMsecs += StopWatch.stopIfStarted();
1104 if (tuple.isPresent()) {
1105 List<Introspector> list = tuple.get().getValue1();
1106 uri = this.getURIFromList(list);
1114 * Gets the URI from list.
1116 * @param list the list
1117 * @return the URI from list
1118 * @throws UnsupportedEncodingException the unsupported encoding exception
1120 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
1122 StringBuilder sb = new StringBuilder();
1123 for (Introspector i : list) {
1124 sb.insert(0, i.getURI());
1127 uri = sb.toString();
1128 return UriBuilder.fromPath(uri).build();
1134 * @param start the start
1135 * @param removeDamaged the remove damaged
1136 * @return the parents
1137 * @throws InstantiationException the instantiation exception
1138 * @throws IllegalAccessException the illegal access exception
1139 * @throws IllegalArgumentException the illegal argument exception
1140 * @throws InvocationTargetException the invocation target exception
1141 * @throws NoSuchMethodException the no such method exception
1142 * @throws SecurityException the security exception
1143 * @throws AAIUnknownObjectException
1145 private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1147 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1148 List<Introspector> objs = new ArrayList<>();
1149 boolean shortCircuit = false;
1150 for (Vertex v : results) {
1151 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1152 Introspector obj = null;
1153 //vertex on the other end of this edge is bad
1154 if (nodeType == null) {
1155 //log something here about what was found and that it was removed
1156 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1157 if (removeDamaged) {
1158 this.deleteWithTraversal(v);
1160 shortCircuit = true;
1163 obj = loader.introspectorFromName(nodeType);
1164 } catch (AAIUnknownObjectException e) {
1165 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1171 //can't make a valid path because we don't understand this object
1174 this.simpleDbToObject(obj, v);
1179 //stop processing and don't return anything for this bad vertex
1181 return Optional.empty();
1184 return Optional.of(new Pair<>(results.get(results.size()-1), objs));
1189 * @throws AAIUnknownObjectException
1190 * @throws IllegalArgumentException elated to property.
1192 * @param relationship the relationship
1193 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1195 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1196 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1197 List<Introspector> relatedToProperties = new ArrayList<>();
1199 if (nameProps != null) {
1200 String[] props = nameProps.split(",");
1201 for (String prop : props) {
1202 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1203 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1204 relatedTo.setValue("property-value", child.getValue(prop));
1205 relatedToProperties.add(relatedTo);
1209 if (!relatedToProperties.isEmpty()) {
1210 List relatedToList = (List)relationship.getValue("related-to-property");
1211 for (Introspector obj : relatedToProperties) {
1212 relatedToList.add(obj.getUnderlyingObject());
1221 * @param relationship the relationship
1222 * @param inputVertex the input vertex
1223 * @return true, if successful
1224 * @throws UnsupportedEncodingException the unsupported encoding exception
1225 * @throws AAIException the AAI exception
1227 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1229 Vertex relatedVertex = null;
1230 StopWatch.conditionalStart();
1231 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1233 String label = null;
1234 if (relationship.hasProperty("relationship-label")) {
1235 label = relationship.getValue("relationship-label");
1238 List<Vertex> results = parser.getQueryBuilder().toList();
1239 if (results.isEmpty()) {
1240 dbTimeMsecs += StopWatch.stopIfStarted();
1241 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1242 e.getTemplateVars().add(parser.getResultType());
1243 e.getTemplateVars().add(parser.getUri().toString());
1246 //still an issue if there's more than one
1247 relatedVertex = results.get(0);
1250 if (relatedVertex != null) {
1254 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1256 edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label);
1258 //attempted to link two vertexes already linked
1261 dbTimeMsecs += StopWatch.stopIfStarted();
1265 dbTimeMsecs += StopWatch.stopIfStarted();
1270 * Gets all the edges between of the type.
1272 * @param aVertex the out vertex
1273 * @param bVertex the in vertex
1274 * @return the edges between
1275 * @throws AAIException the AAI exception
1276 * @throws NoEdgeRuleFoundException
1278 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) {
1280 List<Edge> result = new ArrayList<>();
1282 if (bVertex != null) {
1283 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1284 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE();
1285 if (EdgeType.TREE.equals(type)) {
1286 findEdgesBetween = findEdgesBetween
1289 __.has(EdgeProperty.CONTAINS.toString(), "NONE"),
1290 __.has(EdgeField.PRIVATE.toString(), true)
1294 findEdgesBetween = findEdgesBetween
1295 .has(EdgeProperty.CONTAINS.toString(), "NONE")
1297 __.has(EdgeField.PRIVATE.toString(), true)
1300 findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id()));
1301 result = findEdgesBetween.toList();
1307 * Gets all the edges between the vertexes with the label and type.
1309 * @param aVertex the out vertex
1310 * @param bVertex the in vertex
1312 * @return the edges between
1313 * @throws AAIException the AAI exception
1315 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1317 List<Edge> result = new ArrayList<>();
1319 if (bVertex != null) {
1320 String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value();
1321 String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value();
1322 EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build();
1325 rule = edgeRules.getRule(q);
1326 } catch (EdgeRuleNotFoundException e) {
1327 throw new NoEdgeRuleFoundException(e);
1328 } catch (AmbiguousRuleChoiceException e) {
1329 throw new MultipleEdgeRuleFoundException(e);
1331 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1332 for (Edge edge : edges) {
1333 if (edge.label().equals(rule.getLabel())) {
1343 * Gets the edge between with the label and edge type.
1345 * @param aVertex the out vertex
1346 * @param bVertex the in vertex
1348 * @return the edge between
1349 * @throws AAIException the AAI exception
1350 * @throws NoEdgeRuleFoundException
1352 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
1354 StopWatch.conditionalStart();
1355 if (bVertex != null) {
1357 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex, label);
1359 if (!edges.isEmpty()) {
1360 dbTimeMsecs += StopWatch.stopIfStarted();
1361 return edges.get(0);
1365 dbTimeMsecs += StopWatch.stopIfStarted();
1368 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1369 return this.getEdgeBetween(type, aVertex, bVertex, null);
1376 * @param relationship the relationship
1377 * @param inputVertex the input vertex
1378 * @return true, if successful
1379 * @throws UnsupportedEncodingException the unsupported encoding exception
1380 * @throws AAIException the AAI exception
1382 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1384 Vertex relatedVertex = null;
1385 StopWatch.conditionalStart();
1386 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1388 List<Vertex> results = parser.getQueryBuilder().toList();
1390 String label = null;
1391 if (relationship.hasProperty("relationship-label")) {
1392 label = relationship.getValue("relationship-label");
1395 if (results.isEmpty()) {
1396 dbTimeMsecs += StopWatch.stopIfStarted();
1400 relatedVertex = results.get(0);
1403 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label);
1404 } catch (NoEdgeRuleFoundException e) {
1405 dbTimeMsecs += StopWatch.stopIfStarted();
1406 throw new AAIException("AAI_6129", e);
1410 dbTimeMsecs += StopWatch.stopIfStarted();
1413 dbTimeMsecs += StopWatch.stopIfStarted();
1420 * Delete items with traversal.
1422 * @param vertexes the vertexes
1423 * @throws IllegalStateException the illegal state exception
1425 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1427 for (Vertex v : vertexes) {
1428 LOGGER.debug("About to delete the vertex with id: " + v.id());
1429 deleteWithTraversal(v);
1435 * Delete with traversal.
1437 * @param startVertex the start vertex
1439 public void deleteWithTraversal(Vertex startVertex) {
1440 StopWatch.conditionalStart();
1441 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1443 for (Vertex v : results) {
1444 LOGGER.warn("Removing vertex " + v.id().toString());
1448 dbTimeMsecs += StopWatch.stopIfStarted();
1455 * @param resourceVersion the resource version
1456 * @throws IllegalArgumentException the illegal argument exception
1457 * @throws AAIException the AAI exception
1458 * @throws InterruptedException the interrupted exception
1460 public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1462 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1464 * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain
1465 * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted
1466 * So its better to make these in 2 steps
1468 if(result && !deletableVertices.isEmpty()){
1469 result = verifyPreventDeleteSemantics(deletableVertices);
1474 deleteWithTraversal(v);
1475 } catch (IllegalStateException e) {
1476 throw new AAIException("AAI_6110", e);
1488 * @param resourceVersion the resource version
1489 * @throws IllegalArgumentException the illegal argument exception
1490 * @throws AAIException the AAI exception
1491 * @throws InterruptedException the interrupted exception
1493 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1495 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1500 deleteWithTraversal(v);
1501 } catch (IllegalStateException e) {
1502 throw new AAIException("AAI_6110", e);
1509 * Verify delete semantics.
1511 * @param vertex the vertex
1512 * @param resourceVersion the resource version
1513 * @return true, if successful
1514 * @throws AAIException the AAI exception
1516 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1517 boolean result = true;
1518 String nodeType = "";
1519 String errorDetail = " unknown delete semantic found";
1520 String aaiExceptionCode = "";
1521 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1522 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1524 List<Vertex> vertices = new ArrayList<Vertex>();
1525 vertices.add(vertex);
1526 result = verifyPreventDeleteSemantics(vertices);
1532 * Verify Prevent delete semantics.
1533 * @param vertices the list of vertices
1534 * @return true, if successful
1535 * @throws AAIException the AAI exception
1537 private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException {
1538 boolean result = true;
1539 String nodeType = "";
1540 String errorDetail = " unknown delete semantic found";
1541 String aaiExceptionCode = "";
1543 StopWatch.conditionalStart();
1545 * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition
1546 * If yes - that should prevent the deletion of the vertex
1547 * Dedup makes sure we dont capture the prevent-delete vertices twice
1548 * The prevent-delete vertices are stored so that the error message displays what prevents the delete
1551 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices).
1552 union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE),
1553 __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE))
1556 dbTimeMsecs += StopWatch.stopIfStarted();
1557 if (!preventDeleteVertices.isEmpty()) {
1558 aaiExceptionCode = "AAI_6110";
1559 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);
1563 throw new AAIException(aaiExceptionCode, errorDetail);
1569 * Verify resource version.
1571 * @param action the action
1572 * @param nodeType the node type
1573 * @param currentResourceVersion the current resource version
1574 * @param resourceVersion the resource version
1575 * @param uri the uri
1576 * @return true, if successful
1577 * @throws AAIException the AAI exception
1579 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1580 String enabled = "";
1581 String errorDetail = "";
1582 String aaiExceptionCode = "";
1583 if (currentResourceVersion == null) {
1584 currentResourceVersion = "";
1587 if (resourceVersion == null) {
1588 resourceVersion = "";
1591 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1592 } catch (AAIException e) {
1593 ErrorLogHelper.logException(e);
1595 if (enabled.equals("true")) {
1596 if (!currentResourceVersion.equals(resourceVersion)) {
1597 if (action.equals("create") && !resourceVersion.equals("")) {
1598 errorDetail = "resource-version passed for " + action + " of " + uri;
1599 aaiExceptionCode = "AAI_6135";
1600 } else if (resourceVersion.equals("")) {
1601 errorDetail = "resource-version not passed for " + action + " of " + uri;
1602 aaiExceptionCode = "AAI_6130";
1604 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1605 aaiExceptionCode = "AAI_6131";
1608 throw new AAIException(aaiExceptionCode, errorDetail);
1616 * Convert from camel case.
1618 * @param name the name
1619 * @return the string
1621 private String convertFromCamelCase (String name) {
1623 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1625 NamingExceptions exceptions = NamingExceptions.getInstance();
1626 result = exceptions.getDBName(result);
1631 private boolean canModify(Introspector obj, String propName, String requestContext) {
1632 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1633 if (readOnly != null) {
1634 final String[] items = readOnly.split(",");
1635 for (String item : items) {
1636 if (requestContext.equals(item)) {
1644 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1646 SideEffectRunner runner = new SideEffectRunner
1647 .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build();
1649 runner.execute(obj, self);
1652 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1654 SideEffectRunner runner = new SideEffectRunner
1655 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1657 runner.execute(obj, self);
1660 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1662 SideEffectRunner runner = new SideEffectRunner
1663 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1665 runner.execute(obj, self);
1668 public double getDBTimeMsecs() {
1669 return (dbTimeMsecs);
1673 * Db to object With Filters
1674 * This is for a one-time run with Tenant Isloation to only filter relationships
1675 * TODO: Chnage the original dbToObject to take filter parent/cousins
1677 * @param obj the obj
1678 * @param v the vertex from the graph
1679 * @param depth the depth
1680 * @param nodeOnly specify if to exclude relationships or not
1681 * @param filterCousinNodes
1682 * @return the introspector
1683 * @throws AAIException the AAI exception
1684 * @throws IllegalAccessException the illegal access exception
1685 * @throws IllegalArgumentException the illegal argument exception
1686 * @throws InvocationTargetException the invocation target exception
1687 * @throws SecurityException the security exception
1688 * @throws InstantiationException the instantiation exception
1689 * @throws NoSuchMethodException the no such method exception
1690 * @throws UnsupportedEncodingException the unsupported encoding exception
1691 * @throws MalformedURLException the malformed URL exception
1692 * @throws AAIUnknownObjectException
1693 * @throws URISyntaxException
1695 //TODO - See if you can merge the 2 dbToObjectWithFilters
1696 public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException {
1697 String cleanUp = "false";
1703 boolean modified = false;
1704 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
1705 List<Object> getList = null;
1706 Vertex[] vertices = null;
1708 if (!(obj.isComplexType(property) || obj.isListType(property))) {
1709 this.copySimpleProperty(property, obj, v);
1712 if (obj.isComplexType(property)) {
1713 /* container case */
1715 if (!property.equals("relationship-list") && depth >= 0) {
1716 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
1717 Object result = dbToObjectWithFilters(argumentObject, v, seen, depth+1, nodeOnly, filterCousinNodes, filterParentNodes);
1718 if (result != null) {
1719 obj.setValue(property, argumentObject.getUnderlyingObject());
1722 } else if (property.equals("relationship-list") && !nodeOnly){
1723 /* relationships need to be handled correctly */
1724 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
1725 relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes);
1726 if (relationshipList != null) {
1728 obj.setValue(property, relationshipList.getUnderlyingObject());
1733 } else if (obj.isListType(property)) {
1735 if (property.equals("any")) {
1738 String genericType = obj.getGenericTypeClass(property).getSimpleName();
1739 if (obj.isComplexGenericType(property) && depth >= 0) {
1740 final String childDbName = convertFromCamelCase(genericType);
1741 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1744 boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains);
1746 EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build();
1749 rule = edgeRules.getRule(q);
1750 } catch (EdgeRuleNotFoundException e) {
1751 throw new NoEdgeRuleFoundException(e);
1752 } catch (AmbiguousRuleChoiceException e) {
1753 throw new MultipleEdgeRuleFoundException(e);
1755 if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) {
1756 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
1757 Direction ruleDirection = rule.getDirection();
1758 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
1759 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
1760 itr = verticesList.stream().filter(item -> {
1761 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
1763 if (itr.hasNext()) {
1764 getList = (List<Object>)obj.getValue(property);
1768 while (itr.hasNext()) {
1769 Vertex childVertex = itr.next();
1770 if (!seen.contains(childVertex)) {
1771 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
1773 Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes);
1774 if (result != null) {
1775 getList.add(argumentObject.getUnderlyingObject());
1781 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
1784 if (processed == 0) {
1785 //vertices were all seen, reset the list
1788 if (processed > 0) {
1792 } else if (obj.isSimpleGenericType(property)) {
1793 List<Object> temp = this.engine.getListProperty(v, property);
1795 getList = (List<Object>)obj.getValue(property);
1796 getList.addAll(temp);
1807 //no changes were made to this obj, discard the instance
1811 this.enrichData(obj, v);
1817 * Creates the relationship list with the filtered node types.
1820 * @param obj the obj
1821 * @param cleanUp the clean up
1822 * @return the object
1823 * @throws InstantiationException the instantiation exception
1824 * @throws IllegalAccessException the illegal access exception
1825 * @throws IllegalArgumentException the illegal argument exception
1826 * @throws InvocationTargetException the invocation target exception
1827 * @throws NoSuchMethodException the no such method exception
1828 * @throws SecurityException the security exception
1829 * @throws UnsupportedEncodingException the unsupported encoding exception
1830 * @throws AAIException the AAI exception
1831 * @throws MalformedURLException the malformed URL exception
1832 * @throws URISyntaxException
1834 private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException {
1835 List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v);
1837 Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> {
1838 String node = (String)item.property(AAIProperties.NODE_TYPE).orElse("");
1839 return filterNodes.parallelStream().anyMatch(node::contains);
1843 List<Vertex> cousins = (List<Vertex>)IteratorUtils.toList(cousinVertices);
1845 //items.parallelStream().anyMatch(inputStr::contains)
1846 List<Object> relationshipObjList = obj.getValue("relationship");
1847 for (Vertex cousin : cousins) {
1849 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
1850 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null);
1851 if (result != null) {
1852 relationshipObjList.add(result);
1858 if (relationshipObjList.isEmpty()) {