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.openecomp.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;
37 import java.util.concurrent.Callable;
38 import java.util.concurrent.ExecutionException;
39 import java.util.concurrent.ExecutorService;
40 import java.util.concurrent.Future;
42 import javax.ws.rs.core.UriBuilder;
44 import org.apache.commons.collections.IteratorUtils;
45 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
46 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
47 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
48 import org.apache.tinkerpop.gremlin.structure.Direction;
49 import org.apache.tinkerpop.gremlin.structure.Edge;
50 import org.apache.tinkerpop.gremlin.structure.Element;
51 import org.apache.tinkerpop.gremlin.structure.Vertex;
52 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
53 import org.javatuples.Pair;
55 import org.openecomp.aai.db.props.AAIProperties;
56 import org.openecomp.aai.exceptions.AAIException;
57 import org.openecomp.aai.introspection.Introspector;
58 import org.openecomp.aai.introspection.IntrospectorFactory;
59 import org.openecomp.aai.introspection.Loader;
60 import org.openecomp.aai.introspection.LoaderFactory;
61 import org.openecomp.aai.introspection.ModelType;
62 import org.openecomp.aai.introspection.PropertyPredicates;
63 import org.openecomp.aai.introspection.Version;
64 import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException;
65 import org.openecomp.aai.introspection.sideeffect.DataCopy;
66 import org.openecomp.aai.introspection.sideeffect.DataLinkReader;
67 import org.openecomp.aai.introspection.sideeffect.DataLinkWriter;
68 import org.openecomp.aai.introspection.sideeffect.SideEffectRunner;
69 import org.openecomp.aai.logging.ErrorLogHelper;
70 import org.openecomp.aai.parsers.query.QueryParser;
71 import org.openecomp.aai.parsers.uri.URIParser;
72 import org.openecomp.aai.parsers.uri.URIToRelationshipObject;
73 import org.openecomp.aai.query.builder.QueryBuilder;
74 import org.openecomp.aai.schema.enums.ObjectMetadata;
75 import org.openecomp.aai.schema.enums.PropertyMetadata;
76 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
77 import org.openecomp.aai.serialization.engines.TransactionalGraphEngine;
78 import org.openecomp.aai.serialization.tinkerpop.TreeBackedVertex;
79 import org.openecomp.aai.util.AAIApiServerURLBase;
80 import org.openecomp.aai.util.AAIConfig;
81 import org.openecomp.aai.util.AAIConstants;
82 import org.openecomp.aai.workarounds.NamingExceptions;
83 import com.att.eelf.configuration.EELFLogger;
84 import com.att.eelf.configuration.EELFManager;
85 import com.google.common.base.CaseFormat;
86 import com.thinkaurelius.titan.core.SchemaViolationException;
88 public class DBSerializer {
90 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
92 private final TransactionalGraphEngine engine;
93 private final String sourceOfTruth;
94 private final ModelType introspectionType;
95 private final Version version;
96 private final Loader latestLoader;
97 private final EdgeRules edgeRules = EdgeRules.getInstance();
98 private final Loader loader;
99 private final String baseURL;
101 * Instantiates a new DB serializer.
103 * @param version the version
104 * @param engine the engine
106 * @param introspectionType the introspection type
107 * @param sourceOfTruth the source of truth
108 * @param llBuilder the ll builder
109 * @throws AAIException
111 public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
112 this.engine = engine;
113 this.sourceOfTruth = sourceOfTruth;
114 this.introspectionType = introspectionType;
115 this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST);
116 this.version = version;
117 this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version);
118 this.baseURL = AAIApiServerURLBase.get(version);
122 * Touch standard vertex properties.
125 * @param isNewVertex the is new vertex
128 * to be defined and expanded later
130 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
132 long unixTimeNow = System.currentTimeMillis();
133 String timeNowInSec = "" + unixTimeNow;
135 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
136 v.property(AAIProperties.CREATED_TS, timeNowInSec);
138 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
139 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
140 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
147 * Creates the new vertex.
149 * @param wrappedObject the wrapped object
151 * @throws UnsupportedEncodingException the unsupported encoding exception
152 * @throws AAIException the AAI exception
154 public Vertex createNewVertex(Introspector wrappedObject) {
157 Vertex v = this.engine.tx().addVertex();
158 v.property(AAIProperties.NODE_TYPE, wrappedObject.getDbName());
159 touchStandardVertexProperties(v, true);
167 * @param className the class name
171 * Removes the classpath from a class name
173 public String trimClassName (String className) {
174 String returnValue = "";
176 if (className.lastIndexOf('.') == -1) {
179 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
189 * @param uriQuery the uri query
190 * @param identifier the identifier
191 * @throws SecurityException the security exception
192 * @throws IllegalAccessException the illegal access exception
193 * @throws IllegalArgumentException the illegal argument exception
194 * @throws InvocationTargetException the invocation target exception
195 * @throws InstantiationException the instantiation exception
196 * @throws InterruptedException the interrupted exception
197 * @throws NoSuchMethodException the no such method exception
198 * @throws AAIException the AAI exception
199 * @throws UnsupportedEncodingException the unsupported encoding exception
200 * @throws AAIUnknownObjectException
202 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
205 if (uriQuery.isDependent()) {
206 //try to find the parent
207 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
208 if (!vertices.isEmpty()) {
209 Vertex parent = vertices.get(0);
210 this.reflectDependentVertex(parent, v, obj, requestContext);
212 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
215 serializeSingleVertex(v, obj, requestContext);
218 } catch (SchemaViolationException e) {
219 throw new AAIException("AAI_6117", e);
224 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
226 boolean isTopLevel = obj.isTopLevel();
228 v.property(AAIProperties.AAI_URI, obj.getURI());
230 processObject(obj, v, requestContext);
232 URI uri = this.getURIForVertex(v);
233 URIParser parser = new URIParser(this.loader, uri);
234 if (parser.validate()) {
235 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
236 if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
237 v.property(AAIProperties.AAI_URI, uri.toString());
241 } catch (SchemaViolationException e) {
242 throw new AAIException("AAI_6117", e);
249 * @param <T> the generic type
253 * @throws IllegalAccessException the illegal access exception
254 * @throws IllegalArgumentException the illegal argument exception
255 * @throws InvocationTargetException the invocation target exception
256 * @throws InstantiationException the instantiation exception
257 * @throws NoSuchMethodException the no such method exception
258 * @throws SecurityException the security exception
259 * @throws AAIException the AAI exception
260 * @throws UnsupportedEncodingException the unsupported encoding exception
261 * @throws AAIUnknownObjectException
264 * Helper method for reflectToDb
265 * Handles all the property setting
267 private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
268 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
269 properties.remove(AAIProperties.RESOURCE_VERSION);
270 List<Vertex> dependentVertexes = new ArrayList<>();
271 List<Vertex> processedVertexes = new ArrayList<>();
272 boolean isComplexType = false;
273 boolean isListType = false;
274 this.executePreSideEffects(obj, v);
275 for (String property : properties) {
277 final String propertyType;
278 propertyType = obj.getType(property);
279 isComplexType = obj.isComplexType(property);
280 isListType = obj.isListType(property);
281 value = obj.getValue(property);
283 if (!(isComplexType || isListType)) {
284 boolean canModify = this.canModify(obj, property, requestContext);
287 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
288 String dbProperty = property;
289 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
290 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
292 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
293 //data linked properties are ephemeral
294 //they are populated dynamically on GETs
298 if (propertyType.toLowerCase().contains(".long")) {
299 v.property(dbProperty, new Integer(((Long)value).toString()));
301 v.property(dbProperty, value);
304 v.property(dbProperty).remove();
307 } else if (isListType) {
308 List<Object> list = (List<Object>)value;
309 if (obj.isComplexGenericType(property)) {
311 for (Object o : list) {
312 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
313 child.setURIChain(obj.getURI());
314 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
319 engine.setListProperty(v, property, list);
322 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
323 if (value != null) { //effectively ignore complex properties not included in the object we're processing
324 if (value.getClass().isArray()) {
326 int length = Array.getLength(value);
327 for (int i = 0; i < length; i ++) {
328 Object arrayElement = Array.get(value, i);
329 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
330 child.setURIChain(obj.getURI());
331 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
334 } else if (!property.equals("relationship-list")) {
336 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
337 if (introspector.isContainer()) {
338 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
339 introspector.setURIChain(obj.getURI());
341 processedVertexes.addAll(processObject(introspector, v, requestContext));
344 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
345 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
348 } else if (property.equals("relationship-list")) {
349 handleRelationships(obj, v);
354 this.writeThroughDefaults(v, obj);
355 /* handle those vertexes not touched */
356 for (Vertex toBeRemoved : processedVertexes) {
357 dependentVertexes.remove(toBeRemoved);
359 this.deleteItemsWithTraversal(dependentVertexes);
361 this.executePostSideEffects(obj, v);
362 return processedVertexes;
366 * Handle relationships.
369 * @param vertex the vertex
370 * @throws SecurityException the security exception
371 * @throws IllegalAccessException the illegal access exception
372 * @throws IllegalArgumentException the illegal argument exception
373 * @throws InvocationTargetException the invocation target exception
374 * @throws UnsupportedEncodingException the unsupported encoding exception
375 * @throws AAIException the AAI exception
378 * Handles the explicit relationships defined for an obj
380 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
384 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
385 processRelationshipList(wrappedRl, vertex);
392 * Process relationship list.
394 * @param wrapped the wrapped
396 * @throws UnsupportedEncodingException the unsupported encoding exception
397 * @throws AAIException the AAI exception
399 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
401 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
403 List<Pair<Vertex, Vertex>> addEdges = new ArrayList<>();
404 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
406 for (Object relationship : relationships) {
408 Vertex cousinVertex = null;
409 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
410 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
412 List<Vertex> results = parser.getQueryBuilder().toList();
413 if (results.isEmpty()) {
414 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
415 ex.getTemplateVars().add(parser.getResultType());
416 ex.getTemplateVars().add(parser.getUri().toString());
419 //still an issue if there's more than one
420 cousinVertex = results.get(0);
423 if (cousinVertex != null) {
425 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex);
428 addEdges.add(new Pair<>(v, cousinVertex));
430 existingEdges.remove(e);
432 } catch (NoEdgeRuleFoundException e1) {
433 throw new AAIException("AAI_6145");
438 for (Edge edge : existingEdges) {
441 for (Pair<Vertex, Vertex> pair : addEdges) {
443 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1());
444 } catch (NoEdgeRuleFoundException e) {
445 throw new AAIException("AAI_6129", e);
452 * Write through defaults.
456 * @throws AAIUnknownObjectException
458 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
459 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
460 if (latest != null) {
461 Set<String> required = latest.getRequiredProperties();
463 for (String field : required) {
464 String defaultValue = null;
465 Object vertexProp = null;
466 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
467 if (defaultValue != null) {
468 vertexProp = v.<Object>property(field).orElse(null);
469 if (vertexProp == null) {
470 v.property(field, defaultValue);
480 * Reflect dependent vertex.
483 * @param dependentObj the dependent obj
485 * @throws IllegalAccessException the illegal access exception
486 * @throws IllegalArgumentException the illegal argument exception
487 * @throws InvocationTargetException the invocation target exception
488 * @throws InstantiationException the instantiation exception
489 * @throws NoSuchMethodException the no such method exception
490 * @throws SecurityException the security exception
491 * @throws AAIException the AAI exception
492 * @throws UnsupportedEncodingException the unsupported encoding exception
493 * @throws AAIUnknownObjectException
495 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
497 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
498 //List<Vertex> items = p.getQuery().toList();
499 QueryBuilder query = this.engine.getQueryBuilder(v);
500 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
501 query.createKeyQuery(dependentObj);
503 List<Vertex> items = query.toList();
505 Vertex dependentVertex = null;
506 if (items.size() == 1) {
507 dependentVertex = items.get(0);
508 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
510 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
511 dependentVertex = createNewVertex(dependentObj);
514 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
519 * Reflect dependent vertex.
521 * @param parent the parent
522 * @param child the child
525 * @throws IllegalAccessException the illegal access exception
526 * @throws IllegalArgumentException the illegal argument exception
527 * @throws InvocationTargetException the invocation target exception
528 * @throws InstantiationException the instantiation exception
529 * @throws NoSuchMethodException the no such method exception
530 * @throws SecurityException the security exception
531 * @throws AAIException the AAI exception
532 * @throws UnsupportedEncodingException the unsupported encoding exception
533 * @throws AAIUnknownObjectException
535 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
537 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
538 if (parentUri != null) {
541 child.property(AAIProperties.AAI_URI, parentUri + uri);
543 processObject(obj, child, requestContext);
546 e = this.getEdgeBetween(EdgeType.TREE, parent, child);
548 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
549 if (canBeLinked != null && canBeLinked.equals("true")) {
550 boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
552 child.property(AAIProperties.LINKED, true);
555 edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
564 * @param vertices the vertices
566 * @param depth the depth
567 * @param cleanUp the clean up
568 * @return the introspector
569 * @throws AAIException the AAI exception
570 * @throws IllegalAccessException the illegal access exception
571 * @throws IllegalArgumentException the illegal argument exception
572 * @throws InvocationTargetException the invocation target exception
573 * @throws SecurityException the security exception
574 * @throws InstantiationException the instantiation exception
575 * @throws NoSuchMethodException the no such method exception
576 * @throws UnsupportedEncodingException the unsupported encoding exception
577 * @throws MalformedURLException the malformed URL exception
578 * @throws AAIUnknownObjectException
579 * @throws URISyntaxException
581 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
583 final int internalDepth;
584 if (depth == Integer.MAX_VALUE) {
585 internalDepth = depth--;
587 internalDepth = depth;
589 if (vertices.size() > 1 && !obj.isContainer()) {
590 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
591 } else if (obj.isContainer()) {
593 String listProperty = null;
594 for (String property : obj.getProperties()) {
595 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
596 listProperty = property;
600 final String propertyName = listProperty;
601 getList = (List)obj.getValue(listProperty);
603 /* This is an experimental multithreading experiment
606 ExecutorService pool = GetAllPool.getInstance().getPool();
608 List<Future<Object>> futures = new ArrayList<>();
609 for (Vertex v : vertices) {
610 Callable<Object> task = () -> {
611 Set<Vertex> seen = new HashSet<>();
612 Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
613 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
614 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
615 dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
616 return childObject.getUnderlyingObject();
617 //getList.add(childObject.getUnderlyingObject());
619 futures.add(pool.submit(task));
622 for (Future<Object> future : futures) {
624 getList.add(future.get());
625 } catch (ExecutionException e) {
626 throw new AAIException("AAI_4000", e);
627 } catch (InterruptedException e) {
628 throw new AAIException("AAI_4000", e);
631 } else if (vertices.size() == 1) {
632 Set<Vertex> seen = new HashSet<>();
633 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
634 TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
635 dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
649 * @param seen the seen
650 * @param depth the depth
651 * @param cleanUp the clean up
652 * @return the introspector
653 * @throws IllegalAccessException the illegal access exception
654 * @throws IllegalArgumentException the illegal argument exception
655 * @throws InvocationTargetException the invocation target exception
656 * @throws SecurityException the security exception
657 * @throws InstantiationException the instantiation exception
658 * @throws NoSuchMethodException the no such method exception
659 * @throws UnsupportedEncodingException the unsupported encoding exception
660 * @throws AAIException the AAI exception
661 * @throws MalformedURLException the malformed URL exception
662 * @throws AAIUnknownObjectException
663 * @throws URISyntaxException
665 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
673 boolean modified = false;
674 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
675 List<Object> getList = null;
676 Vertex[] vertices = null;
678 if (!(obj.isComplexType(property) || obj.isListType(property))) {
679 this.copySimpleProperty(property, obj, v);
682 if (obj.isComplexType(property)) {
685 if (!property.equals("relationship-list") && depth >= 0) {
686 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
687 Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
688 if (result != null) {
689 obj.setValue(property, argumentObject.getUnderlyingObject());
692 } else if (property.equals("relationship-list") && !nodeOnly){
693 /* relationships need to be handled correctly */
694 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
695 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
696 if (relationshipList != null) {
698 obj.setValue(property, relationshipList.getUnderlyingObject());
703 } else if (obj.isListType(property)) {
705 if (property.equals("any")) {
708 String genericType = obj.getGenericTypeClass(property).getSimpleName();
709 if (obj.isComplexGenericType(property) && depth >= 0) {
710 final String childDbName = convertFromCamelCase(genericType);
711 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
714 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
715 if (rule.getIsParent().equals("true") || rule.getIsParent().equals("reverse")) {
716 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
717 Direction ruleDirection = rule.getDirection();
718 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
719 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
720 itr = verticesList.stream().filter(item -> {
721 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
724 getList = (List<Object>)obj.getValue(property);
728 while (itr.hasNext()) {
729 Vertex childVertex = itr.next();
730 if (!seen.contains(childVertex)) {
731 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
733 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
734 if (result != null) {
735 getList.add(argumentObject.getUnderlyingObject());
741 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
744 if (processed == 0) {
745 //vertices were all seen, reset the list
752 } else if (obj.isSimpleGenericType(property)) {
753 List<Object> temp = this.engine.getListProperty(v, property);
755 getList = (List<Object>)obj.getValue(property);
756 getList.addAll(temp);
767 //no changes were made to this obj, discard the instance
771 this.enrichData(obj, v);
777 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
778 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
779 if (nodeType == null) {
780 throw new AAIException("AAI_6143");
782 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
783 Set<Vertex> seen = new HashSet<>();
785 String cleanUp = "false";
786 boolean nodeOnly = true;
787 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
792 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
793 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
794 if (nodeType == null) {
795 throw new AAIException("AAI_6143");
797 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
798 Set<Vertex> seen = new HashSet<>();
799 int depth = AAIProperties.MAXIMUM_DEPTH;
800 String cleanUp = "false";
801 boolean nodeOnly = false;
802 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
803 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
804 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
809 * Copy simple property.
811 * @param property the property
814 * @throws InstantiationException the instantiation exception
815 * @throws IllegalAccessException the illegal access exception
816 * @throws IllegalArgumentException the illegal argument exception
817 * @throws InvocationTargetException the invocation target exception
818 * @throws NoSuchMethodException the no such method exception
819 * @throws SecurityException the security exception
821 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
822 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
823 String dbPropertyName = property;
824 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
825 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
827 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
830 obj.setValue(property, temp);
835 * Simple db to object.
839 * @throws InstantiationException the instantiation exception
840 * @throws IllegalAccessException the illegal access exception
841 * @throws IllegalArgumentException the illegal argument exception
842 * @throws InvocationTargetException the invocation target exception
843 * @throws NoSuchMethodException the no such method exception
844 * @throws SecurityException the security exception
846 private void simpleDbToObject (Introspector obj, Vertex v) {
847 for (String property : obj.getProperties()) {
850 if (!(obj.isComplexType(property) || obj.isListType(property))) {
851 this.copySimpleProperty(property, obj, v);
857 * Creates the relationship list.
861 * @param cleanUp the clean up
863 * @throws InstantiationException the instantiation exception
864 * @throws IllegalAccessException the illegal access exception
865 * @throws IllegalArgumentException the illegal argument exception
866 * @throws InvocationTargetException the invocation target exception
867 * @throws NoSuchMethodException the no such method exception
868 * @throws SecurityException the security exception
869 * @throws UnsupportedEncodingException the unsupported encoding exception
870 * @throws AAIException the AAI exception
871 * @throws MalformedURLException the malformed URL exception
872 * @throws URISyntaxException
874 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
877 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
879 List<Object> relationshipObjList = obj.getValue("relationship");
881 for (Vertex cousin : cousins) {
883 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
884 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp);
885 if (result != null) {
886 relationshipObjList.add(result);
891 if (relationshipObjList.isEmpty()) {
899 * Process edge relationship.
901 * @param relationshipObj the relationship obj
902 * @param edge the edge
903 * @param direction the direction
904 * @param cleanUp the clean up
906 * @throws InstantiationException the instantiation exception
907 * @throws IllegalAccessException the illegal access exception
908 * @throws IllegalArgumentException the illegal argument exception
909 * @throws InvocationTargetException the invocation target exception
910 * @throws NoSuchMethodException the no such method exception
911 * @throws SecurityException the security exception
912 * @throws UnsupportedEncodingException the unsupported encoding exception
913 * @throws AAIException the AAI exception
914 * @throws MalformedURLException the malformed URL exception
915 * @throws AAIUnknownObjectException
916 * @throws URISyntaxException
918 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException {
921 //we must look up all parents in this case because we need to compute name-properties
922 //we cannot used the cached aaiUri to perform this action currently
923 Pair<Vertex, List<Introspector>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
924 //damaged vertex found, ignore
928 List<Introspector> list = tuple.getValue1();
929 URI uri = this.getURIFromList(list);
931 URIToRelationshipObject uriParser = null;
932 Introspector result = null;
934 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
935 result = uriParser.getResult();
936 } catch (AAIException | URISyntaxException e) {
937 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.getValue0().id().toString() + ": " + e.getMessage(), e);
938 if ("true".equals(cleanUp)) {
939 this.deleteWithTraversal(tuple.getValue0());
944 if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){
945 this.addRelatedToProperty(result, list.get(0));
948 return result.getUnderlyingObject();
952 * Gets the URI for vertex.
955 * @return the URI for vertex
956 * @throws InstantiationException the instantiation exception
957 * @throws IllegalAccessException the illegal access exception
958 * @throws IllegalArgumentException the illegal argument exception
959 * @throws InvocationTargetException the invocation target exception
960 * @throws NoSuchMethodException the no such method exception
961 * @throws SecurityException the security exception
962 * @throws UnsupportedEncodingException the unsupported encoding exception
963 * @throws AAIUnknownObjectException
965 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
967 return getURIForVertex(v, false);
970 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
973 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
975 if (aaiUri != null && !overwrite) {
976 uri = UriBuilder.fromPath(aaiUri).build();
978 Pair<Vertex, List<Introspector>> tuple = this.getParents(this.loader, v, false);
979 List<Introspector> list = tuple.getValue1();
980 uri = this.getURIFromList(list);
985 * Gets the URI from list.
987 * @param list the list
988 * @return the URI from list
989 * @throws UnsupportedEncodingException the unsupported encoding exception
991 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
993 StringBuilder sb = new StringBuilder();
994 for (Introspector i : list) {
995 sb.insert(0, i.getURI());
999 URI result = UriBuilder.fromPath(uri).build();
1006 * @param start the start
1007 * @param removeDamaged the remove damaged
1008 * @return the parents
1009 * @throws InstantiationException the instantiation exception
1010 * @throws IllegalAccessException the illegal access exception
1011 * @throws IllegalArgumentException the illegal argument exception
1012 * @throws InvocationTargetException the invocation target exception
1013 * @throws NoSuchMethodException the no such method exception
1014 * @throws SecurityException the security exception
1015 * @throws AAIUnknownObjectException
1017 private Pair<Vertex, List<Introspector>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1019 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1020 List<Introspector> objs = new ArrayList<>();
1021 boolean shortCircuit = false;
1022 for (Vertex v : results) {
1023 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1024 Introspector obj = null;
1025 //vertex on the other end of this edge is bad
1026 if (nodeType == null) {
1027 //log something here about what was found and that it was removed
1028 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1029 if (removeDamaged) {
1030 this.deleteWithTraversal(v);
1032 shortCircuit = true;
1035 obj = loader.introspectorFromName(nodeType);
1036 } catch (AAIUnknownObjectException e) {
1037 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1043 //can't make a valid path because we don't understand this object
1046 this.simpleDbToObject(obj, v);
1051 //stop processing and don't return anything for this bad vertex
1056 return new Pair<>(results.get(results.size()-1), objs);
1059 * Takes a list of vertices and a list of objs and assumes they are in
1060 * the order you want the URIs to be nested.
1061 * [A,B,C] creates uris [A, AB, ABC]
1064 * @throws UnsupportedEncodingException
1065 * @throws URISyntaxException
1067 public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1069 String uriChain = "";
1070 for (int i = 0; i < vertices.size(); i++) {
1073 v = vertices.get(i);
1074 aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1075 if (aaiUri != null) {
1078 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1079 aaiUri = uri.toString();
1081 v.property(AAIProperties.AAI_URI, uriChain);
1092 * @throws AAIUnknownObjectException
1093 * @throws IllegalArgumentException elated to property.
1095 * @param relationship the relationship
1096 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1098 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1099 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1100 List<Introspector> relatedToProperties = new ArrayList<>();
1102 if (nameProps != null) {
1103 String[] props = nameProps.split(",");
1104 for (String prop : props) {
1105 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1106 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1107 relatedTo.setValue("property-value", child.getValue(prop));
1108 relatedToProperties.add(relatedTo);
1112 if (relatedToProperties.size() > 0) {
1113 List relatedToList = (List)relationship.getValue("related-to-property");
1114 for (Introspector obj : relatedToProperties) {
1115 relatedToList.add(obj.getUnderlyingObject());
1124 * @param relationship the relationship
1125 * @param inputVertex the input vertex
1126 * @return true, if successful
1127 * @throws UnsupportedEncodingException the unsupported encoding exception
1128 * @throws AAIException the AAI exception
1130 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1132 Vertex relatedVertex = null;
1134 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1136 List<Vertex> results = parser.getQueryBuilder().toList();
1137 if (results.size() == 0) {
1138 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1139 e.getTemplateVars().add(parser.getResultType());
1140 e.getTemplateVars().add(parser.getUri().toString());
1143 //still an issue if there's more than one
1144 relatedVertex = results.get(0);
1147 if (relatedVertex != null) {
1151 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1153 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex);
1156 //attempted to link two vertexes already linked
1158 } catch (NoEdgeRuleFoundException e1) {
1159 throw new AAIException("AAI_6129", e1);
1171 * Gets the edges between.
1173 * @param aVertex the out vertex
1174 * @param bVertex the in vertex
1175 * @return the edges between
1176 * @throws AAIException the AAI exception
1177 * @throws NoEdgeRuleFoundException
1179 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException {
1181 List<Edge> result = new ArrayList<>();
1183 if (bVertex != null) {
1184 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex);
1185 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1186 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id()));
1187 List<Edge> edges = findEdgesBetween.toList();
1188 for (Edge edge : edges) {
1189 if (edge.label().equals(rule.getLabel())) {
1200 * Gets the edge between.
1202 * @param aVertex the out vertex
1203 * @param bVertex the in vertex
1204 * @return the edge between
1205 * @throws AAIException the AAI exception
1206 * @throws NoEdgeRuleFoundException
1208 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1212 if (bVertex != null) {
1214 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1216 if (edges.size() > 0) {
1217 return edges.get(0);
1229 * @param relationship the relationship
1230 * @param inputVertex the input vertex
1231 * @return true, if successful
1232 * @throws UnsupportedEncodingException the unsupported encoding exception
1233 * @throws AAIException the AAI exception
1235 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1237 Vertex relatedVertex = null;
1239 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1241 List<Vertex> results = parser.getQueryBuilder().toList();
1243 if (results.size() == 0) {
1247 relatedVertex = results.get(0);
1250 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1251 } catch (NoEdgeRuleFoundException e) {
1252 throw new AAIException("AAI_6129", e);
1264 * Delete items with traversal.
1266 * @param vertexes the vertexes
1267 * @throws IllegalStateException the illegal state exception
1269 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1270 for (Vertex v : vertexes) {
1271 LOGGER.debug("About to delete the vertex with id: " + v.id());
1272 deleteWithTraversal(v);
1277 * Delete with traversal.
1279 * @param startVertex the start vertex
1281 public void deleteWithTraversal(Vertex startVertex) {
1283 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1285 for (Vertex v : results) {
1286 LOGGER.warn("Removing vertex " + v.id().toString());
1297 * @param resourceVersion the resource version
1298 * @throws IllegalArgumentException the illegal argument exception
1299 * @throws AAIException the AAI exception
1300 * @throws InterruptedException the interrupted exception
1302 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1304 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1307 deleteWithTraversal(v);
1308 } catch (IllegalStateException e) {
1309 throw new AAIException("AAI_6110", e);
1318 * Verify delete semantics.
1320 * @param vertex the vertex
1321 * @param resourceVersion the resource version
1322 * @return true, if successful
1323 * @throws AAIException the AAI exception
1325 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1326 boolean result = false;
1327 String nodeType = "";
1328 DeleteSemantic semantic = null;
1329 List<Edge> inEdges = null;
1330 List<Edge> outEdges = null;
1331 String errorDetail = " unknown delete semantic found";
1332 String aaiExceptionCode = "";
1333 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1334 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1336 semantic = edgeRules.getDeleteSemantic(nodeType);
1337 inEdges = (List<Edge>)IteratorUtils.toList(vertex.edges(Direction.IN));
1338 outEdges = (List<Edge>)IteratorUtils.toList(vertex.edges(Direction.OUT));
1339 if (semantic.equals(DeleteSemantic.CASCADE_TO_CHILDREN)) {
1341 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_EDGES)) {
1342 if (inEdges.isEmpty() && outEdges.isEmpty()) {
1345 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1346 aaiExceptionCode = "AAI_6110";
1348 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_IN_EDGES) || semantic.equals(DeleteSemantic.ERROR_4_IN_EDGES_OR_CASCADE)) {
1350 if (inEdges.isEmpty()) {
1353 //are there any cousin edges?
1355 for (Edge e : inEdges) {
1356 if (e.<Boolean>property("isParent").orElse(false)) {
1361 result = children == inEdges.size();
1365 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1366 aaiExceptionCode = "AAI_6110";
1368 } else if (semantic.equals(DeleteSemantic.THIS_NODE_ONLY)) {
1369 if (outEdges.isEmpty() && inEdges.isEmpty()) {
1373 for (Edge edge : outEdges) {
1374 Object property = edge.<Boolean>property("isParent").orElse(null);
1375 if (property != null && property.equals(Boolean.TRUE)) {
1376 Vertex v = edge.inVertex();
1377 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1378 errorDetail = " Node cannot be deleted using scope = " + semantic +
1379 " another node (type = " + vType + ") depends on it for uniqueness.";
1380 aaiExceptionCode = "AAI_6110";
1386 for (Edge edge : inEdges) {
1387 Object property = edge.<Boolean>property("isParent-REV").orElse(null);
1388 if (property != null && property.equals(Boolean.TRUE)) {
1389 Vertex v = edge.outVertex();
1390 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1391 errorDetail = " Node cannot be deleted using scope = " + semantic +
1392 " another node (type = " + vType + ") depends on it for uniqueness.";
1393 aaiExceptionCode = "AAI_6110";
1403 throw new AAIException(aaiExceptionCode, errorDetail);
1409 * Verify resource version.
1411 * @param action the action
1412 * @param nodeType the node type
1413 * @param currentResourceVersion the current resource version
1414 * @param resourceVersion the resource version
1415 * @param uri the uri
1416 * @return true, if successful
1417 * @throws AAIException the AAI exception
1419 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1420 String enabled = "";
1421 String errorDetail = "";
1422 String aaiExceptionCode = "";
1423 if (currentResourceVersion == null) {
1424 currentResourceVersion = "";
1427 if (resourceVersion == null) {
1428 resourceVersion = "";
1431 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1432 } catch (AAIException e) {
1433 ErrorLogHelper.logException(e);
1435 // We're only doing the resource version checks for v5 and later
1436 if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) {
1437 if (!currentResourceVersion.equals(resourceVersion)) {
1438 if (action.equals("create") && !resourceVersion.equals("")) {
1439 errorDetail = "resource-version passed for " + action + " of " + uri;
1440 aaiExceptionCode = "AAI_6135";
1441 } else if (resourceVersion.equals("")) {
1442 errorDetail = "resource-version not passed for " + action + " of " + uri;
1443 aaiExceptionCode = "AAI_6130";
1445 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1446 aaiExceptionCode = "AAI_6131";
1449 throw new AAIException(aaiExceptionCode, errorDetail);
1457 * Convert from camel case.
1459 * @param name the name
1460 * @return the string
1462 private String convertFromCamelCase (String name) {
1464 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1466 NamingExceptions exceptions = NamingExceptions.getInstance();
1467 result = exceptions.getDBName(result);
1472 private boolean canModify(Introspector obj, String propName, String requestContext) {
1473 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1474 if (readOnly != null) {
1475 final String[] items = readOnly.split(",");
1476 for (String item : items) {
1477 if (requestContext.equals(item)) {
1485 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1487 SideEffectRunner runner = new SideEffectRunner
1488 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1490 runner.execute(obj, self);
1493 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1495 SideEffectRunner runner = new SideEffectRunner
1496 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1498 runner.execute(obj, self);
1501 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1503 SideEffectRunner runner = new SideEffectRunner
1504 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1506 runner.execute(obj, self);