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 com.att.eelf.configuration.EELFLogger;
25 import com.att.eelf.configuration.EELFManager;
26 import com.google.common.base.CaseFormat;
27 import com.thinkaurelius.titan.core.SchemaViolationException;
28 import org.apache.commons.collections.IteratorUtils;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
30 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
31 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
32 import org.apache.tinkerpop.gremlin.structure.*;
33 import org.javatuples.Pair;
34 import org.openecomp.aai.db.props.AAIProperties;
35 import org.openecomp.aai.exceptions.AAIException;
36 import org.openecomp.aai.introspection.*;
37 import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException;
38 import org.openecomp.aai.introspection.sideeffect.DataCopy;
39 import org.openecomp.aai.introspection.sideeffect.DataLinkReader;
40 import org.openecomp.aai.introspection.sideeffect.DataLinkWriter;
41 import org.openecomp.aai.introspection.sideeffect.SideEffectRunner;
42 import org.openecomp.aai.logging.ErrorLogHelper;
43 import org.openecomp.aai.parsers.query.QueryParser;
44 import org.openecomp.aai.parsers.uri.URIParser;
45 import org.openecomp.aai.parsers.uri.URIToRelationshipObject;
46 import org.openecomp.aai.query.builder.QueryBuilder;
47 import org.openecomp.aai.schema.enums.ObjectMetadata;
48 import org.openecomp.aai.schema.enums.PropertyMetadata;
49 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
50 import org.openecomp.aai.serialization.engines.TransactionalGraphEngine;
51 import org.openecomp.aai.serialization.tinkerpop.TreeBackedVertex;
52 import org.openecomp.aai.util.AAIApiServerURLBase;
53 import org.openecomp.aai.util.AAIConfig;
54 import org.openecomp.aai.util.AAIConstants;
55 import org.openecomp.aai.workarounds.NamingExceptions;
57 import javax.ws.rs.core.UriBuilder;
58 import java.io.UnsupportedEncodingException;
59 import java.lang.reflect.Array;
60 import java.lang.reflect.InvocationTargetException;
61 import java.net.MalformedURLException;
63 import java.net.URISyntaxException;
65 import java.util.concurrent.Callable;
66 import java.util.concurrent.ExecutionException;
67 import java.util.concurrent.ExecutorService;
68 import java.util.concurrent.Future;
70 public class DBSerializer {
72 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class);
74 private final TransactionalGraphEngine engine;
75 private final String sourceOfTruth;
76 private final ModelType introspectionType;
77 private final Version version;
78 private final Loader latestLoader;
79 private final EdgeRules edgeRules = EdgeRules.getInstance();
80 private final Loader loader;
81 private final String baseURL;
83 * Instantiates a new DB serializer.
85 * @param version the version
86 * @param engine the engine
88 * @param introspectionType the introspection type
89 * @param sourceOfTruth the source of truth
90 * @param llBuilder the ll builder
91 * @throws AAIException
93 public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException {
95 this.sourceOfTruth = sourceOfTruth;
96 this.introspectionType = introspectionType;
97 this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST);
98 this.version = version;
99 this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version);
100 this.baseURL = AAIApiServerURLBase.get(version);
104 * Touch standard vertex properties.
107 * @param isNewVertex the is new vertex
109 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
111 long unixTimeNow = System.currentTimeMillis();
112 String timeNowInSec = "" + unixTimeNow;
114 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
115 v.property(AAIProperties.CREATED_TS, timeNowInSec);
117 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
118 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
119 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
123 private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) {
125 v.property(AAIProperties.NODE_TYPE, nodeType);
126 touchStandardVertexProperties(v, isNewVertex);
133 * Creates the new vertex.
135 * @param wrappedObject the wrapped object
137 * @throws UnsupportedEncodingException the unsupported encoding exception
138 * @throws AAIException the AAI exception
140 public Vertex createNewVertex(Introspector wrappedObject) {
143 Vertex v = this.engine.tx().addVertex();
144 touchStandardVertexProperties(wrappedObject.getDbName(), v, true);
152 * @param className the class name
156 * Removes the classpath from a class name
158 public String trimClassName (String className) {
159 String returnValue = "";
161 if (className.lastIndexOf('.') == -1) {
164 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
174 * @param uriQuery the uri query
175 * @param identifier the identifier
176 * @throws SecurityException the security exception
177 * @throws IllegalAccessException the illegal access exception
178 * @throws IllegalArgumentException the illegal argument exception
179 * @throws InvocationTargetException the invocation target exception
180 * @throws InstantiationException the instantiation exception
181 * @throws InterruptedException the interrupted exception
182 * @throws NoSuchMethodException the no such method exception
183 * @throws AAIException the AAI exception
184 * @throws UnsupportedEncodingException the unsupported encoding exception
185 * @throws AAIUnknownObjectException
187 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
190 if (uriQuery.isDependent()) {
191 //try to find the parent
192 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
193 if (!vertices.isEmpty()) {
194 Vertex parent = vertices.get(0);
195 this.reflectDependentVertex(parent, v, obj, requestContext);
197 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
200 serializeSingleVertex(v, obj, requestContext);
203 } catch (SchemaViolationException e) {
204 throw new AAIException("AAI_6117", e);
209 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
211 boolean isTopLevel = obj.isTopLevel();
213 v.property(AAIProperties.AAI_URI, obj.getURI());
215 processObject(obj, v, requestContext);
217 URI uri = this.getURIForVertex(v);
218 URIParser parser = new URIParser(this.loader, uri);
219 if (parser.validate()) {
220 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
221 if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
222 v.property(AAIProperties.AAI_URI, uri.toString());
226 } catch (SchemaViolationException e) {
227 throw new AAIException("AAI_6117", e);
234 * @param <T> the generic type
238 * @throws IllegalAccessException the illegal access exception
239 * @throws IllegalArgumentException the illegal argument exception
240 * @throws InvocationTargetException the invocation target exception
241 * @throws InstantiationException the instantiation exception
242 * @throws NoSuchMethodException the no such method exception
243 * @throws SecurityException the security exception
244 * @throws AAIException the AAI exception
245 * @throws UnsupportedEncodingException the unsupported encoding exception
246 * @throws AAIUnknownObjectException
249 * Helper method for reflectToDb
250 * Handles all the property setting
252 private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
253 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
254 properties.remove(AAIProperties.RESOURCE_VERSION);
255 List<Vertex> dependentVertexes = new ArrayList<>();
256 List<Vertex> processedVertexes = new ArrayList<>();
257 boolean isComplexType = false;
258 boolean isListType = false;
259 if (!obj.isContainer()) {
260 this.touchStandardVertexProperties(obj.getDbName(), v, false);
262 this.executePreSideEffects(obj, v);
263 for (String property : properties) {
265 final String propertyType;
266 propertyType = obj.getType(property);
267 isComplexType = obj.isComplexType(property);
268 isListType = obj.isListType(property);
269 value = obj.getValue(property);
271 if (!(isComplexType || isListType)) {
272 boolean canModify = this.canModify(obj, property, requestContext);
275 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
276 String dbProperty = property;
277 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
278 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
280 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
281 //data linked properties are ephemeral
282 //they are populated dynamically on GETs
286 if (!value.equals(v.property(dbProperty).orElse(null))) {
287 if (propertyType.toLowerCase().contains(".long")) {
288 v.property(dbProperty, new Integer(((Long)value).toString()));
290 v.property(dbProperty, value);
294 v.property(dbProperty).remove();
297 } else if (isListType) {
298 List<Object> list = (List<Object>)value;
299 if (obj.isComplexGenericType(property)) {
301 for (Object o : list) {
302 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
303 child.setURIChain(obj.getURI());
304 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
309 engine.setListProperty(v, property, list);
312 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
313 if (value != null) { //effectively ignore complex properties not included in the object we're processing
314 if (value.getClass().isArray()) {
316 int length = Array.getLength(value);
317 for (int i = 0; i < length; i ++) {
318 Object arrayElement = Array.get(value, i);
319 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
320 child.setURIChain(obj.getURI());
321 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
324 } else if (!property.equals("relationship-list")) {
326 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
327 if (introspector.isContainer()) {
328 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
329 introspector.setURIChain(obj.getURI());
331 processedVertexes.addAll(processObject(introspector, v, requestContext));
334 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
335 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
338 } else if (property.equals("relationship-list")) {
339 handleRelationships(obj, v);
344 this.writeThroughDefaults(v, obj);
345 /* handle those vertexes not touched */
346 for (Vertex toBeRemoved : processedVertexes) {
347 dependentVertexes.remove(toBeRemoved);
349 this.deleteItemsWithTraversal(dependentVertexes);
351 this.executePostSideEffects(obj, v);
352 return processedVertexes;
356 * Handle relationships.
359 * @param vertex the vertex
360 * @throws SecurityException the security exception
361 * @throws IllegalAccessException the illegal access exception
362 * @throws IllegalArgumentException the illegal argument exception
363 * @throws InvocationTargetException the invocation target exception
364 * @throws UnsupportedEncodingException the unsupported encoding exception
365 * @throws AAIException the AAI exception
368 * Handles the explicit relationships defined for an obj
370 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
374 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
375 processRelationshipList(wrappedRl, vertex);
382 * Process relationship list.
384 * @param wrapped the wrapped
386 * @throws UnsupportedEncodingException the unsupported encoding exception
387 * @throws AAIException the AAI exception
389 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
391 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
393 List<Pair<Vertex, Vertex>> addEdges = new ArrayList<>();
394 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
396 for (Object relationship : relationships) {
398 Vertex cousinVertex = null;
399 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
400 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
402 List<Vertex> results = parser.getQueryBuilder().toList();
403 if (results.isEmpty()) {
404 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
405 ex.getTemplateVars().add(parser.getResultType());
406 ex.getTemplateVars().add(parser.getUri().toString());
409 //still an issue if there's more than one
410 cousinVertex = results.get(0);
413 if (cousinVertex != null) {
415 if (!edgeRules.hasEdgeRule(v, cousinVertex)) {
416 throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", "
417 + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + ".");
419 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex);
422 addEdges.add(new Pair<>(v, cousinVertex));
424 existingEdges.remove(e);
426 } catch (NoEdgeRuleFoundException e1) {
427 throw new AAIException("AAI_6145");
432 for (Edge edge : existingEdges) {
435 for (Pair<Vertex, Vertex> pair : addEdges) {
437 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1());
438 } catch (NoEdgeRuleFoundException e) {
439 throw new AAIException("AAI_6129", e);
446 * Write through defaults.
450 * @throws AAIUnknownObjectException
452 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
453 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
454 if (latest != null) {
455 Set<String> required = latest.getRequiredProperties();
457 for (String field : required) {
458 String defaultValue = null;
459 Object vertexProp = null;
460 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
461 if (defaultValue != null) {
462 vertexProp = v.<Object>property(field).orElse(null);
463 if (vertexProp == null) {
464 v.property(field, defaultValue);
474 * Reflect dependent vertex.
477 * @param dependentObj the dependent obj
479 * @throws IllegalAccessException the illegal access exception
480 * @throws IllegalArgumentException the illegal argument exception
481 * @throws InvocationTargetException the invocation target exception
482 * @throws InstantiationException the instantiation exception
483 * @throws NoSuchMethodException the no such method exception
484 * @throws SecurityException the security exception
485 * @throws AAIException the AAI exception
486 * @throws UnsupportedEncodingException the unsupported encoding exception
487 * @throws AAIUnknownObjectException
489 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
491 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
492 //List<Vertex> items = p.getQuery().toList();
493 QueryBuilder query = this.engine.getQueryBuilder(v);
494 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
495 query.createKeyQuery(dependentObj);
497 List<Vertex> items = query.toList();
499 Vertex dependentVertex = null;
500 if (items.size() == 1) {
501 dependentVertex = items.get(0);
502 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
504 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
505 dependentVertex = createNewVertex(dependentObj);
508 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
513 * Reflect dependent vertex.
515 * @param parent the parent
516 * @param child the child
519 * @throws IllegalAccessException the illegal access exception
520 * @throws IllegalArgumentException the illegal argument exception
521 * @throws InvocationTargetException the invocation target exception
522 * @throws InstantiationException the instantiation exception
523 * @throws NoSuchMethodException the no such method exception
524 * @throws SecurityException the security exception
525 * @throws AAIException the AAI exception
526 * @throws UnsupportedEncodingException the unsupported encoding exception
527 * @throws AAIUnknownObjectException
529 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
531 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
532 if (parentUri != null) {
535 child.property(AAIProperties.AAI_URI, parentUri + uri);
537 processObject(obj, child, requestContext);
540 e = this.getEdgeBetween(EdgeType.TREE, parent, child);
542 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
543 if (canBeLinked != null && canBeLinked.equals("true")) {
544 boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
546 child.property(AAIProperties.LINKED, true);
549 edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
558 * @param vertices the vertices
560 * @param depth the depth
561 * @param cleanUp the clean up
562 * @return the introspector
563 * @throws AAIException the AAI exception
564 * @throws IllegalAccessException the illegal access exception
565 * @throws IllegalArgumentException the illegal argument exception
566 * @throws InvocationTargetException the invocation target exception
567 * @throws SecurityException the security exception
568 * @throws InstantiationException the instantiation exception
569 * @throws NoSuchMethodException the no such method exception
570 * @throws UnsupportedEncodingException the unsupported encoding exception
571 * @throws MalformedURLException the malformed URL exception
572 * @throws AAIUnknownObjectException
573 * @throws URISyntaxException
575 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
577 final int internalDepth;
578 if (depth == Integer.MAX_VALUE) {
579 internalDepth = depth--;
581 internalDepth = depth;
583 if (vertices.size() > 1 && !obj.isContainer()) {
584 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
585 } else if (obj.isContainer()) {
587 String listProperty = null;
588 for (String property : obj.getProperties()) {
589 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
590 listProperty = property;
594 final String propertyName = listProperty;
595 getList = (List)obj.getValue(listProperty);
597 /* This is an experimental multithreading experiment
600 ExecutorService pool = GetAllPool.getInstance().getPool();
602 List<Future<Object>> futures = new ArrayList<>();
603 for (Vertex v : vertices) {
604 Callable<Object> task = () -> {
605 Set<Vertex> seen = new HashSet<>();
606 Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
607 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
608 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
609 dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
610 return childObject.getUnderlyingObject();
611 //getList.add(childObject.getUnderlyingObject());
613 futures.add(pool.submit(task));
616 for (Future<Object> future : futures) {
618 getList.add(future.get());
619 } catch (ExecutionException e) {
620 throw new AAIException("AAI_4000", e);
621 } catch (InterruptedException e) {
622 throw new AAIException("AAI_4000", e);
625 } else if (vertices.size() == 1) {
626 Set<Vertex> seen = new HashSet<>();
627 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
628 TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
629 dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
643 * @param seen the seen
644 * @param depth the depth
645 * @param cleanUp the clean up
646 * @return the introspector
647 * @throws IllegalAccessException the illegal access exception
648 * @throws IllegalArgumentException the illegal argument exception
649 * @throws InvocationTargetException the invocation target exception
650 * @throws SecurityException the security exception
651 * @throws InstantiationException the instantiation exception
652 * @throws NoSuchMethodException the no such method exception
653 * @throws UnsupportedEncodingException the unsupported encoding exception
654 * @throws AAIException the AAI exception
655 * @throws MalformedURLException the malformed URL exception
656 * @throws AAIUnknownObjectException
657 * @throws URISyntaxException
659 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
667 boolean modified = false;
668 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
669 List<Object> getList = null;
670 Vertex[] vertices = null;
672 if (!(obj.isComplexType(property) || obj.isListType(property))) {
673 this.copySimpleProperty(property, obj, v);
676 if (obj.isComplexType(property)) {
679 if (!property.equals("relationship-list") && depth >= 0) {
680 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
681 Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
682 if (result != null) {
683 obj.setValue(property, argumentObject.getUnderlyingObject());
686 } else if (property.equals("relationship-list") && !nodeOnly){
687 /* relationships need to be handled correctly */
688 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
689 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
690 if (relationshipList != null) {
692 obj.setValue(property, relationshipList.getUnderlyingObject());
697 } else if (obj.isListType(property)) {
699 if (property.equals("any")) {
702 String genericType = obj.getGenericTypeClass(property).getSimpleName();
703 if (obj.isComplexGenericType(property) && depth >= 0) {
704 final String childDbName = convertFromCamelCase(genericType);
705 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
708 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
709 if (!rule.getContains().equals(AAIDirection.NONE.toString())) {
710 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
711 Direction ruleDirection = rule.getDirection();
712 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
713 List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr);
714 itr = verticesList.stream().filter(item -> {
715 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
718 getList = (List<Object>)obj.getValue(property);
722 while (itr.hasNext()) {
723 Vertex childVertex = itr.next();
724 if (!seen.contains(childVertex)) {
725 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
727 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
728 if (result != null) {
729 getList.add(argumentObject.getUnderlyingObject());
735 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
738 if (processed == 0) {
739 //vertices were all seen, reset the list
746 } else if (obj.isSimpleGenericType(property)) {
747 List<Object> temp = this.engine.getListProperty(v, property);
749 getList = (List<Object>)obj.getValue(property);
750 getList.addAll(temp);
761 //no changes were made to this obj, discard the instance
765 this.enrichData(obj, v);
771 public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException {
772 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
773 if (nodeType == null) {
774 throw new AAIException("AAI_6143");
776 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
777 Set<Vertex> seen = new HashSet<>();
779 String cleanUp = "false";
780 boolean nodeOnly = true;
781 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
786 public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException {
787 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
788 if (nodeType == null) {
789 throw new AAIException("AAI_6143");
791 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
792 Set<Vertex> seen = new HashSet<>();
793 int depth = AAIProperties.MAXIMUM_DEPTH;
794 String cleanUp = "false";
795 boolean nodeOnly = false;
796 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
797 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
798 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
803 * Copy simple property.
805 * @param property the property
808 * @throws InstantiationException the instantiation exception
809 * @throws IllegalAccessException the illegal access exception
810 * @throws IllegalArgumentException the illegal argument exception
811 * @throws InvocationTargetException the invocation target exception
812 * @throws NoSuchMethodException the no such method exception
813 * @throws SecurityException the security exception
815 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
816 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
817 String dbPropertyName = property;
818 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
819 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
821 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
824 obj.setValue(property, temp);
829 * Simple db to object.
833 * @throws InstantiationException the instantiation exception
834 * @throws IllegalAccessException the illegal access exception
835 * @throws IllegalArgumentException the illegal argument exception
836 * @throws InvocationTargetException the invocation target exception
837 * @throws NoSuchMethodException the no such method exception
838 * @throws SecurityException the security exception
840 private void simpleDbToObject (Introspector obj, Vertex v) {
841 for (String property : obj.getProperties()) {
844 if (!(obj.isComplexType(property) || obj.isListType(property))) {
845 this.copySimpleProperty(property, obj, v);
851 * Creates the relationship list.
855 * @param cleanUp the clean up
857 * @throws InstantiationException the instantiation exception
858 * @throws IllegalAccessException the illegal access exception
859 * @throws IllegalArgumentException the illegal argument exception
860 * @throws InvocationTargetException the invocation target exception
861 * @throws NoSuchMethodException the no such method exception
862 * @throws SecurityException the security exception
863 * @throws UnsupportedEncodingException the unsupported encoding exception
864 * @throws AAIException the AAI exception
865 * @throws MalformedURLException the malformed URL exception
866 * @throws URISyntaxException
868 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
871 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
873 List<Object> relationshipObjList = obj.getValue("relationship");
875 for (Vertex cousin : cousins) {
877 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
878 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp);
879 if (result != null) {
880 relationshipObjList.add(result);
885 if (relationshipObjList.isEmpty()) {
893 * Process edge relationship.
895 * @param relationshipObj the relationship obj
896 * @param edge the edge
897 * @param direction the direction
898 * @param cleanUp the clean up
900 * @throws InstantiationException the instantiation exception
901 * @throws IllegalAccessException the illegal access exception
902 * @throws IllegalArgumentException the illegal argument exception
903 * @throws InvocationTargetException the invocation target exception
904 * @throws NoSuchMethodException the no such method exception
905 * @throws SecurityException the security exception
906 * @throws UnsupportedEncodingException the unsupported encoding exception
907 * @throws AAIException the AAI exception
908 * @throws MalformedURLException the malformed URL exception
909 * @throws AAIUnknownObjectException
910 * @throws URISyntaxException
912 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException {
915 //we must look up all parents in this case because we need to compute name-properties
916 //we cannot used the cached aaiUri to perform this action currently
917 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
918 //damaged vertex found, ignore
919 if (!tuple.isPresent()) {
922 List<Introspector> list = tuple.get().getValue1();
923 URI uri = this.getURIFromList(list);
925 URIToRelationshipObject uriParser = null;
926 Introspector result = null;
928 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
929 result = uriParser.getResult();
930 } catch (AAIException | URISyntaxException e) {
931 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " + e.getMessage(), e);
932 if ("true".equals(cleanUp)) {
933 this.deleteWithTraversal(tuple.get().getValue0());
938 if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){
939 this.addRelatedToProperty(result, list.get(0));
942 return result.getUnderlyingObject();
946 * Gets the URI for vertex.
949 * @return the URI for vertex
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
956 * @throws UnsupportedEncodingException the unsupported encoding exception
957 * @throws AAIUnknownObjectException
959 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
961 return getURIForVertex(v, false);
964 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
965 URI uri = UriBuilder.fromPath("/unknown-uri").build();
967 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
969 if (aaiUri != null && !overwrite) {
970 uri = UriBuilder.fromPath(aaiUri).build();
972 Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false);
973 if (tuple.isPresent()) {
974 List<Introspector> list = tuple.get().getValue1();
975 uri = this.getURIFromList(list);
981 * Gets the URI from list.
983 * @param list the list
984 * @return the URI from list
985 * @throws UnsupportedEncodingException the unsupported encoding exception
987 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
989 StringBuilder sb = new StringBuilder();
990 for (Introspector i : list) {
991 sb.insert(0, i.getURI());
995 URI result = UriBuilder.fromPath(uri).build();
1002 * @param start the start
1003 * @param removeDamaged the remove damaged
1004 * @return the parents
1005 * @throws InstantiationException the instantiation exception
1006 * @throws IllegalAccessException the illegal access exception
1007 * @throws IllegalArgumentException the illegal argument exception
1008 * @throws InvocationTargetException the invocation target exception
1009 * @throws NoSuchMethodException the no such method exception
1010 * @throws SecurityException the security exception
1011 * @throws AAIUnknownObjectException
1013 private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
1015 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1016 List<Introspector> objs = new ArrayList<>();
1017 boolean shortCircuit = false;
1018 for (Vertex v : results) {
1019 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1020 Introspector obj = null;
1021 //vertex on the other end of this edge is bad
1022 if (nodeType == null) {
1023 //log something here about what was found and that it was removed
1024 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1025 if (removeDamaged) {
1026 this.deleteWithTraversal(v);
1028 shortCircuit = true;
1031 obj = loader.introspectorFromName(nodeType);
1032 } catch (AAIUnknownObjectException e) {
1033 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1039 //can't make a valid path because we don't understand this object
1042 this.simpleDbToObject(obj, v);
1047 //stop processing and don't return anything for this bad vertex
1049 return Optional.empty();
1052 return Optional.of(new Pair<>(results.get(results.size()-1), objs));
1055 * Takes a list of vertices and a list of objs and assumes they are in
1056 * the order you want the URIs to be nested.
1057 * [A,B,C] creates uris [A, AB, ABC]
1060 * @throws UnsupportedEncodingException
1061 * @throws URISyntaxException
1063 public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1065 String uriChain = "";
1066 for (int i = 0; i < vertices.size(); i++) {
1069 v = vertices.get(i);
1070 aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1071 if (aaiUri != null) {
1074 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1075 aaiUri = uri.toString();
1077 v.property(AAIProperties.AAI_URI, uriChain);
1088 * @throws AAIUnknownObjectException
1089 * @throws IllegalArgumentException elated to property.
1091 * @param relationship the relationship
1092 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1094 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1095 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1096 List<Introspector> relatedToProperties = new ArrayList<>();
1098 if (nameProps != null) {
1099 String[] props = nameProps.split(",");
1100 for (String prop : props) {
1101 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1102 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1103 relatedTo.setValue("property-value", child.getValue(prop));
1104 relatedToProperties.add(relatedTo);
1108 if (relatedToProperties.size() > 0) {
1109 List relatedToList = (List)relationship.getValue("related-to-property");
1110 for (Introspector obj : relatedToProperties) {
1111 relatedToList.add(obj.getUnderlyingObject());
1120 * @param relationship the relationship
1121 * @param inputVertex the input vertex
1122 * @return true, if successful
1123 * @throws UnsupportedEncodingException the unsupported encoding exception
1124 * @throws AAIException the AAI exception
1126 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1128 Vertex relatedVertex = null;
1130 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1132 List<Vertex> results = parser.getQueryBuilder().toList();
1133 if (results.size() == 0) {
1134 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1135 e.getTemplateVars().add(parser.getResultType());
1136 e.getTemplateVars().add(parser.getUri().toString());
1139 //still an issue if there's more than one
1140 relatedVertex = results.get(0);
1143 if (relatedVertex != null) {
1147 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1149 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex);
1152 //attempted to link two vertexes already linked
1154 } catch (NoEdgeRuleFoundException e1) {
1155 throw new AAIException("AAI_6129", e1);
1167 * Gets the edges between.
1169 * @param aVertex the out vertex
1170 * @param bVertex the in vertex
1171 * @return the edges between
1172 * @throws AAIException the AAI exception
1173 * @throws NoEdgeRuleFoundException
1175 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException {
1177 List<Edge> result = new ArrayList<>();
1179 if (bVertex != null) {
1180 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex);
1181 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1182 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id()));
1183 List<Edge> edges = findEdgesBetween.toList();
1184 for (Edge edge : edges) {
1185 if (edge.label().equals(rule.getLabel())) {
1196 * Gets the edge between.
1198 * @param aVertex the out vertex
1199 * @param bVertex the in vertex
1200 * @return the edge between
1201 * @throws AAIException the AAI exception
1202 * @throws NoEdgeRuleFoundException
1204 public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1208 if (bVertex != null) {
1210 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1212 if (edges.size() > 0) {
1213 return edges.get(0);
1225 * @param relationship the relationship
1226 * @param inputVertex the input vertex
1227 * @return true, if successful
1228 * @throws UnsupportedEncodingException the unsupported encoding exception
1229 * @throws AAIException the AAI exception
1231 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1233 Vertex relatedVertex = null;
1235 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1237 List<Vertex> results = parser.getQueryBuilder().toList();
1239 if (results.size() == 0) {
1243 relatedVertex = results.get(0);
1246 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1247 } catch (NoEdgeRuleFoundException e) {
1248 throw new AAIException("AAI_6129", e);
1260 * Delete items with traversal.
1262 * @param vertexes the vertexes
1263 * @throws IllegalStateException the illegal state exception
1265 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1266 for (Vertex v : vertexes) {
1267 LOGGER.debug("About to delete the vertex with id: " + v.id());
1268 deleteWithTraversal(v);
1273 * Delete with traversal.
1275 * @param startVertex the start vertex
1277 public void deleteWithTraversal(Vertex startVertex) {
1279 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1281 for (Vertex v : results) {
1282 LOGGER.warn("Removing vertex " + v.id().toString());
1293 * @param resourceVersion the resource version
1294 * @throws IllegalArgumentException the illegal argument exception
1295 * @throws AAIException the AAI exception
1296 * @throws InterruptedException the interrupted exception
1298 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1300 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1303 deleteWithTraversal(v);
1304 } catch (IllegalStateException e) {
1305 throw new AAIException("AAI_6110", e);
1314 * Verify delete semantics.
1316 * @param vertex the vertex
1317 * @param resourceVersion the resource version
1318 * @return true, if successful
1319 * @throws AAIException the AAI exception
1321 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1322 boolean result = true;
1323 String nodeType = "";
1324 String errorDetail = " unknown delete semantic found";
1325 String aaiExceptionCode = "";
1326 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1327 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1329 List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertex).union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)).dedup().toList();
1331 if (preventDeleteVertices.size() > 0) {
1332 aaiExceptionCode = "AAI_6110";
1333 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);
1337 throw new AAIException(aaiExceptionCode, errorDetail);
1343 * Verify resource version.
1345 * @param action the action
1346 * @param nodeType the node type
1347 * @param currentResourceVersion the current resource version
1348 * @param resourceVersion the resource version
1349 * @param uri the uri
1350 * @return true, if successful
1351 * @throws AAIException the AAI exception
1353 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1354 String enabled = "";
1355 String errorDetail = "";
1356 String aaiExceptionCode = "";
1357 if (currentResourceVersion == null) {
1358 currentResourceVersion = "";
1361 if (resourceVersion == null) {
1362 resourceVersion = "";
1365 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1366 } catch (AAIException e) {
1367 ErrorLogHelper.logException(e);
1369 // We're only doing the resource version checks for v5 and later
1370 if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) {
1371 if (!currentResourceVersion.equals(resourceVersion)) {
1372 if (action.equals("create") && !resourceVersion.equals("")) {
1373 errorDetail = "resource-version passed for " + action + " of " + uri;
1374 aaiExceptionCode = "AAI_6135";
1375 } else if (resourceVersion.equals("")) {
1376 errorDetail = "resource-version not passed for " + action + " of " + uri;
1377 aaiExceptionCode = "AAI_6130";
1379 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1380 aaiExceptionCode = "AAI_6131";
1383 throw new AAIException(aaiExceptionCode, errorDetail);
1391 * Convert from camel case.
1393 * @param name the name
1394 * @return the string
1396 private String convertFromCamelCase (String name) {
1398 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1400 NamingExceptions exceptions = NamingExceptions.getInstance();
1401 result = exceptions.getDBName(result);
1406 private boolean canModify(Introspector obj, String propName, String requestContext) {
1407 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1408 if (readOnly != null) {
1409 final String[] items = readOnly.split(",");
1410 for (String item : items) {
1411 if (requestContext.equals(item)) {
1419 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1421 SideEffectRunner runner = new SideEffectRunner
1422 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1424 runner.execute(obj, self);
1427 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1429 SideEffectRunner runner = new SideEffectRunner
1430 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1432 runner.execute(obj, self);
1435 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1437 SideEffectRunner runner = new SideEffectRunner
1438 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1440 runner.execute(obj, self);