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
110 * to be defined and expanded later
112 public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) {
114 long unixTimeNow = System.currentTimeMillis();
115 String timeNowInSec = "" + unixTimeNow;
117 v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth);
118 v.property(AAIProperties.CREATED_TS, timeNowInSec);
120 v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec );
121 v.property(AAIProperties.LAST_MOD_TS, timeNowInSec);
122 v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth);
129 * Creates the new vertex.
131 * @param wrappedObject the wrapped object
133 * @throws UnsupportedEncodingException the unsupported encoding exception
134 * @throws AAIException the AAI exception
136 public Vertex createNewVertex(Introspector wrappedObject) {
139 Vertex v = this.engine.tx().addVertex();
140 v.property(AAIProperties.NODE_TYPE, wrappedObject.getDbName());
141 touchStandardVertexProperties(v, true);
149 * @param className the class name
153 * Removes the classpath from a class name
155 public String trimClassName (String className) {
156 String returnValue = "";
158 if (className.lastIndexOf('.') == -1) {
161 returnValue = className.substring(className.lastIndexOf('.') + 1, className.length());
171 * @param uriQuery the uri query
172 * @param identifier the identifier
173 * @throws SecurityException the security exception
174 * @throws IllegalAccessException the illegal access exception
175 * @throws IllegalArgumentException the illegal argument exception
176 * @throws InvocationTargetException the invocation target exception
177 * @throws InstantiationException the instantiation exception
178 * @throws InterruptedException the interrupted exception
179 * @throws NoSuchMethodException the no such method exception
180 * @throws AAIException the AAI exception
181 * @throws UnsupportedEncodingException the unsupported encoding exception
182 * @throws AAIUnknownObjectException
184 public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException {
187 if (uriQuery.isDependent()) {
188 //try to find the parent
189 List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList();
190 if (!vertices.isEmpty()) {
191 Vertex parent = vertices.get(0);
192 this.reflectDependentVertex(parent, v, obj, requestContext);
194 throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier);
197 serializeSingleVertex(v, obj, requestContext);
200 } catch (SchemaViolationException e) {
201 throw new AAIException("AAI_6117", e);
206 public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException {
208 boolean isTopLevel = obj.isTopLevel();
210 v.property(AAIProperties.AAI_URI, obj.getURI());
212 processObject(obj, v, requestContext);
214 URI uri = this.getURIForVertex(v);
215 URIParser parser = new URIParser(this.loader, uri);
216 if (parser.validate()) {
217 VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI);
218 if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) {
219 v.property(AAIProperties.AAI_URI, uri.toString());
223 } catch (SchemaViolationException e) {
224 throw new AAIException("AAI_6117", e);
231 * @param <T> the generic type
235 * @throws IllegalAccessException the illegal access exception
236 * @throws IllegalArgumentException the illegal argument exception
237 * @throws InvocationTargetException the invocation target exception
238 * @throws InstantiationException the instantiation exception
239 * @throws NoSuchMethodException the no such method exception
240 * @throws SecurityException the security exception
241 * @throws AAIException the AAI exception
242 * @throws UnsupportedEncodingException the unsupported encoding exception
243 * @throws AAIUnknownObjectException
246 * Helper method for reflectToDb
247 * Handles all the property setting
249 private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException {
250 Set<String> properties = new LinkedHashSet<>(obj.getProperties());
251 properties.remove(AAIProperties.RESOURCE_VERSION);
252 List<Vertex> dependentVertexes = new ArrayList<>();
253 List<Vertex> processedVertexes = new ArrayList<>();
254 boolean isComplexType = false;
255 boolean isListType = false;
256 this.executePreSideEffects(obj, v);
257 for (String property : properties) {
259 final String propertyType;
260 propertyType = obj.getType(property);
261 isComplexType = obj.isComplexType(property);
262 isListType = obj.isListType(property);
263 value = obj.getValue(property);
265 if (!(isComplexType || isListType)) {
266 boolean canModify = this.canModify(obj, property, requestContext);
269 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
270 String dbProperty = property;
271 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
272 dbProperty = metadata.get(PropertyMetadata.DB_ALIAS);
274 if (metadata.containsKey(PropertyMetadata.DATA_LINK)) {
275 //data linked properties are ephemeral
276 //they are populated dynamically on GETs
280 if (propertyType.toLowerCase().contains(".long")) {
281 v.property(dbProperty, new Integer(((Long)value).toString()));
283 v.property(dbProperty, value);
286 v.property(dbProperty).remove();
289 } else if (isListType) {
290 List<Object> list = (List<Object>)value;
291 if (obj.isComplexGenericType(property)) {
293 for (Object o : list) {
294 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o);
295 child.setURIChain(obj.getURI());
296 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
301 engine.setListProperty(v, property, list);
304 //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method
305 if (value != null) { //effectively ignore complex properties not included in the object we're processing
306 if (value.getClass().isArray()) {
308 int length = Array.getLength(value);
309 for (int i = 0; i < length; i ++) {
310 Object arrayElement = Array.get(value, i);
311 Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement);
312 child.setURIChain(obj.getURI());
313 processedVertexes.add(reflectDependentVertex(v, child, requestContext));
316 } else if (!property.equals("relationship-list")) {
318 Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value);
319 if (introspector.isContainer()) {
320 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName()));
321 introspector.setURIChain(obj.getURI());
323 processedVertexes.addAll(processObject(introspector, v, requestContext));
326 dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName()));
327 processedVertexes.add(reflectDependentVertex(v, introspector, requestContext));
330 } else if (property.equals("relationship-list")) {
331 handleRelationships(obj, v);
336 this.writeThroughDefaults(v, obj);
337 /* handle those vertexes not touched */
338 for (Vertex toBeRemoved : processedVertexes) {
339 dependentVertexes.remove(toBeRemoved);
341 this.deleteItemsWithTraversal(dependentVertexes);
343 this.executePostSideEffects(obj, v);
344 return processedVertexes;
348 * Handle relationships.
351 * @param vertex the vertex
352 * @throws SecurityException the security exception
353 * @throws IllegalAccessException the illegal access exception
354 * @throws IllegalArgumentException the illegal argument exception
355 * @throws InvocationTargetException the invocation target exception
356 * @throws UnsupportedEncodingException the unsupported encoding exception
357 * @throws AAIException the AAI exception
360 * Handles the explicit relationships defined for an obj
362 private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException {
366 Introspector wrappedRl = obj.getWrappedValue("relationship-list");
367 processRelationshipList(wrappedRl, vertex);
374 * Process relationship list.
376 * @param wrapped the wrapped
378 * @throws UnsupportedEncodingException the unsupported encoding exception
379 * @throws AAIException the AAI exception
381 private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException {
383 List<Object> relationships = (List<Object>)wrapped.getValue("relationship");
385 List<Pair<Vertex, Vertex>> addEdges = new ArrayList<>();
386 List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader());
388 for (Object relationship : relationships) {
390 Vertex cousinVertex = null;
391 Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship);
392 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel);
394 List<Vertex> results = parser.getQueryBuilder().toList();
395 if (results.isEmpty()) {
396 final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
397 ex.getTemplateVars().add(parser.getResultType());
398 ex.getTemplateVars().add(parser.getUri().toString());
401 //still an issue if there's more than one
402 cousinVertex = results.get(0);
405 if (cousinVertex != null) {
407 e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex);
410 addEdges.add(new Pair<>(v, cousinVertex));
412 existingEdges.remove(e);
414 } catch (NoEdgeRuleFoundException e1) {
415 throw new AAIException("AAI_6145");
420 for (Edge edge : existingEdges) {
423 for (Pair<Vertex, Vertex> pair : addEdges) {
425 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1());
426 } catch (NoEdgeRuleFoundException e) {
427 throw new AAIException("AAI_6129", e);
434 * Write through defaults.
438 * @throws AAIUnknownObjectException
440 private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException {
441 Introspector latest = this.latestLoader.introspectorFromName(obj.getName());
442 if (latest != null) {
443 Set<String> required = latest.getRequiredProperties();
445 for (String field : required) {
446 String defaultValue = null;
447 Object vertexProp = null;
448 defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE);
449 if (defaultValue != null) {
450 vertexProp = v.<Object>property(field).orElse(null);
451 if (vertexProp == null) {
452 v.property(field, defaultValue);
462 * Reflect dependent vertex.
465 * @param dependentObj the dependent obj
467 * @throws IllegalAccessException the illegal access exception
468 * @throws IllegalArgumentException the illegal argument exception
469 * @throws InvocationTargetException the invocation target exception
470 * @throws InstantiationException the instantiation exception
471 * @throws NoSuchMethodException the no such method exception
472 * @throws SecurityException the security exception
473 * @throws AAIException the AAI exception
474 * @throws UnsupportedEncodingException the unsupported encoding exception
475 * @throws AAIUnknownObjectException
477 private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException {
479 //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI());
480 //List<Vertex> items = p.getQuery().toList();
481 QueryBuilder query = this.engine.getQueryBuilder(v);
482 query.createEdgeTraversal(EdgeType.TREE, v, dependentObj);
483 query.createKeyQuery(dependentObj);
485 List<Vertex> items = query.toList();
487 Vertex dependentVertex = null;
488 if (items.size() == 1) {
489 dependentVertex = items.get(0);
490 this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
492 this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI());
493 dependentVertex = createNewVertex(dependentObj);
496 return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext);
501 * Reflect dependent vertex.
503 * @param parent the parent
504 * @param child the child
507 * @throws IllegalAccessException the illegal access exception
508 * @throws IllegalArgumentException the illegal argument exception
509 * @throws InvocationTargetException the invocation target exception
510 * @throws InstantiationException the instantiation exception
511 * @throws NoSuchMethodException the no such method exception
512 * @throws SecurityException the security exception
513 * @throws AAIException the AAI exception
514 * @throws UnsupportedEncodingException the unsupported encoding exception
515 * @throws AAIUnknownObjectException
517 private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException {
519 String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null);
520 if (parentUri != null) {
521 child.property(AAIProperties.AAI_URI, parentUri + obj.getURI());
523 processObject(obj, child, requestContext);
526 e = this.getEdgeBetween(EdgeType.TREE, parent, child);
528 String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED);
529 if (canBeLinked != null && canBeLinked.equals("true")) {
530 boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext();
532 child.property(AAIProperties.LINKED, true);
535 edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child);
544 * @param vertices the vertices
546 * @param depth the depth
547 * @param cleanUp the clean up
548 * @return the introspector
549 * @throws AAIException the AAI exception
550 * @throws IllegalAccessException the illegal access exception
551 * @throws IllegalArgumentException the illegal argument exception
552 * @throws InvocationTargetException the invocation target exception
553 * @throws SecurityException the security exception
554 * @throws InstantiationException the instantiation exception
555 * @throws NoSuchMethodException the no such method exception
556 * @throws UnsupportedEncodingException the unsupported encoding exception
557 * @throws MalformedURLException the malformed URL exception
558 * @throws AAIUnknownObjectException
559 * @throws URISyntaxException
561 public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException {
563 final int internalDepth;
564 if (depth == Integer.MAX_VALUE) {
565 internalDepth = depth--;
567 internalDepth = depth;
569 if (vertices.size() > 1 && !obj.isContainer()) {
570 throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName());
571 } else if (obj.isContainer()) {
573 String listProperty = null;
574 for (String property : obj.getProperties()) {
575 if (obj.isListType(property) && obj.isComplexGenericType(property)) {
576 listProperty = property;
580 final String propertyName = listProperty;
581 getList = (List)obj.getValue(listProperty);
583 /* This is an experimental multithreading experiment
586 ExecutorService pool = GetAllPool.getInstance().getPool();
588 List<Future<Object>> futures = new ArrayList<>();
589 for (Vertex v : vertices) {
590 Callable<Object> task = () -> {
591 Set<Vertex> seen = new HashSet<>();
592 Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName);
593 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly);
594 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
595 dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp);
596 return childObject.getUnderlyingObject();
597 //getList.add(childObject.getUnderlyingObject());
599 futures.add(pool.submit(task));
602 for (Future<Object> future : futures) {
604 getList.add(future.get());
605 } catch (ExecutionException e) {
606 throw new AAIException("AAI_4000", e);
607 } catch (InterruptedException e) {
608 throw new AAIException("AAI_4000", e);
611 } else if (vertices.size() == 1) {
612 Set<Vertex> seen = new HashSet<>();
613 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly);
614 TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree);
615 dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
629 * @param seen the seen
630 * @param depth the depth
631 * @param cleanUp the clean up
632 * @return the introspector
633 * @throws IllegalAccessException the illegal access exception
634 * @throws IllegalArgumentException the illegal argument exception
635 * @throws InvocationTargetException the invocation target exception
636 * @throws SecurityException the security exception
637 * @throws InstantiationException the instantiation exception
638 * @throws NoSuchMethodException the no such method exception
639 * @throws UnsupportedEncodingException the unsupported encoding exception
640 * @throws AAIException the AAI exception
641 * @throws MalformedURLException the malformed URL exception
642 * @throws AAIUnknownObjectException
643 * @throws URISyntaxException
645 private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException {
653 boolean modified = false;
654 for (String property : obj.getProperties(PropertyPredicates.isVisible())) {
655 List<Object> getList = null;
656 Vertex[] vertices = null;
658 if (!(obj.isComplexType(property) || obj.isListType(property))) {
659 this.copySimpleProperty(property, obj, v);
662 if (obj.isComplexType(property)) {
665 if (!property.equals("relationship-list") && depth >= 0) {
666 Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property);
667 Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp);
668 if (result != null) {
669 obj.setValue(property, argumentObject.getUnderlyingObject());
672 } else if (property.equals("relationship-list") && !nodeOnly){
673 /* relationships need to be handled correctly */
674 Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property);
675 relationshipList = createRelationshipList(v, relationshipList, cleanUp);
676 if (relationshipList != null) {
678 obj.setValue(property, relationshipList.getUnderlyingObject());
683 } else if (obj.isListType(property)) {
685 if (property.equals("any")) {
688 String genericType = obj.getGenericTypeClass(property).getSimpleName();
689 if (obj.isComplexGenericType(property) && depth >= 0) {
690 final String childDbName = convertFromCamelCase(genericType);
691 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
694 rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName);
695 if (rule.getIsParent().equals("true") || rule.getIsParent().equals("reverse")) {
696 //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName);
697 Direction ruleDirection = rule.getDirection();
698 Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel());
699 List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr);
700 itr = verticesList.stream().filter(item -> {
701 return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName);
704 getList = (List<Object>)obj.getValue(property);
708 while (itr.hasNext()) {
709 Vertex childVertex = itr.next();
710 if (!seen.contains(childVertex)) {
711 Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property);
713 Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp);
714 if (result != null) {
715 getList.add(argumentObject.getUnderlyingObject());
721 LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString());
724 if (processed == 0) {
725 //vertices were all seen, reset the list
732 } else if (obj.isSimpleGenericType(property)) {
733 List<Object> temp = this.engine.getListProperty(v, property);
735 getList = (List<Object>)obj.getValue(property);
736 getList.addAll(temp);
747 //no changes were made to this obj, discard the instance
751 this.enrichData(obj, v);
757 public Introspector getVertexProperties(Vertex v) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException {
758 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
759 if (nodeType == null) {
760 throw new AAIException("AAI_6143");
762 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
763 Set<Vertex> seen = new HashSet<>();
765 String cleanUp = "false";
766 boolean nodeOnly = true;
767 this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp);
772 public Introspector getLatestVersionView(Vertex v) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException {
773 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
774 if (nodeType == null) {
775 throw new AAIException("AAI_6143");
777 Introspector obj = this.latestLoader.introspectorFromName(nodeType);
778 Set<Vertex> seen = new HashSet<>();
779 int depth = AAIProperties.MAXIMUM_DEPTH;
780 String cleanUp = "false";
781 boolean nodeOnly = false;
782 Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly);
783 TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree);
784 this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp);
789 * Copy simple property.
791 * @param property the property
794 * @throws InstantiationException the instantiation exception
795 * @throws IllegalAccessException the illegal access exception
796 * @throws IllegalArgumentException the illegal argument exception
797 * @throws InvocationTargetException the invocation target exception
798 * @throws NoSuchMethodException the no such method exception
799 * @throws SecurityException the security exception
801 private void copySimpleProperty(String property, Introspector obj, Vertex v) {
802 final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property);
803 String dbPropertyName = property;
804 if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) {
805 dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS);
807 final Object temp = v.<Object>property(dbPropertyName).orElse(null);
810 obj.setValue(property, temp);
815 * Simple db to object.
819 * @throws InstantiationException the instantiation exception
820 * @throws IllegalAccessException the illegal access exception
821 * @throws IllegalArgumentException the illegal argument exception
822 * @throws InvocationTargetException the invocation target exception
823 * @throws NoSuchMethodException the no such method exception
824 * @throws SecurityException the security exception
826 private void simpleDbToObject (Introspector obj, Vertex v) {
827 for (String property : obj.getProperties()) {
830 if (!(obj.isComplexType(property) || obj.isListType(property))) {
831 this.copySimpleProperty(property, obj, v);
837 * Creates the relationship list.
841 * @param cleanUp the clean up
843 * @throws InstantiationException the instantiation exception
844 * @throws IllegalAccessException the illegal access exception
845 * @throws IllegalArgumentException the illegal argument exception
846 * @throws InvocationTargetException the invocation target exception
847 * @throws NoSuchMethodException the no such method exception
848 * @throws SecurityException the security exception
849 * @throws UnsupportedEncodingException the unsupported encoding exception
850 * @throws AAIException the AAI exception
851 * @throws MalformedURLException the malformed URL exception
852 * @throws URISyntaxException
854 private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException {
857 List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v);
859 List<Object> relationshipObjList = obj.getValue("relationship");
861 for (Vertex cousin : cousins) {
863 Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship");
864 Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp);
865 if (result != null) {
866 relationshipObjList.add(result);
871 if (relationshipObjList.isEmpty()) {
879 * Process edge relationship.
881 * @param relationshipObj the relationship obj
882 * @param edge the edge
883 * @param direction the direction
884 * @param cleanUp the clean up
886 * @throws InstantiationException the instantiation exception
887 * @throws IllegalAccessException the illegal access exception
888 * @throws IllegalArgumentException the illegal argument exception
889 * @throws InvocationTargetException the invocation target exception
890 * @throws NoSuchMethodException the no such method exception
891 * @throws SecurityException the security exception
892 * @throws UnsupportedEncodingException the unsupported encoding exception
893 * @throws AAIException the AAI exception
894 * @throws MalformedURLException the malformed URL exception
895 * @throws AAIUnknownObjectException
896 * @throws URISyntaxException
898 private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException {
901 //we must look up all parents in this case because we need to compute name-properties
902 //we cannot used the cached aaiUri to perform this action currently
903 Pair<Vertex, List<Introspector>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp));
904 //damaged vertex found, ignore
908 List<Introspector> list = tuple.getValue1();
909 URI uri = this.getURIFromList(list);
911 URIToRelationshipObject uriParser = null;
912 Introspector result = null;
914 uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL);
915 result = uriParser.getResult();
916 } catch (AAIException | URISyntaxException e) {
917 LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.getValue0().id().toString() + ": " + e.getMessage(), e);
918 if ("true".equals(cleanUp)) {
919 this.deleteWithTraversal(tuple.getValue0());
923 return result.getUnderlyingObject();
927 * Gets the URI for vertex.
930 * @return the URI for vertex
931 * @throws InstantiationException the instantiation exception
932 * @throws IllegalAccessException the illegal access exception
933 * @throws IllegalArgumentException the illegal argument exception
934 * @throws InvocationTargetException the invocation target exception
935 * @throws NoSuchMethodException the no such method exception
936 * @throws SecurityException the security exception
937 * @throws UnsupportedEncodingException the unsupported encoding exception
938 * @throws AAIUnknownObjectException
940 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
942 return getURIForVertex(v, false);
945 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
948 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
950 if (aaiUri != null && !overwrite) {
951 uri = UriBuilder.fromPath(aaiUri).build();
953 Pair<Vertex, List<Introspector>> tuple = this.getParents(this.loader, v, false);
954 List<Introspector> list = tuple.getValue1();
955 uri = this.getURIFromList(list);
960 * Gets the URI from list.
962 * @param list the list
963 * @return the URI from list
964 * @throws UnsupportedEncodingException the unsupported encoding exception
966 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
968 StringBuilder sb = new StringBuilder();
969 for (Introspector i : list) {
970 sb.insert(0, i.getURI());
974 URI result = UriBuilder.fromPath(uri).build();
981 * @param start the start
982 * @param removeDamaged the remove damaged
983 * @return the parents
984 * @throws InstantiationException the instantiation exception
985 * @throws IllegalAccessException the illegal access exception
986 * @throws IllegalArgumentException the illegal argument exception
987 * @throws InvocationTargetException the invocation target exception
988 * @throws NoSuchMethodException the no such method exception
989 * @throws SecurityException the security exception
990 * @throws AAIUnknownObjectException
992 private Pair<Vertex, List<Introspector>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
994 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
995 List<Introspector> objs = new ArrayList<>();
996 boolean shortCircuit = false;
997 for (Vertex v : results) {
998 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
999 Introspector obj = null;
1000 //vertex on the other end of this edge is bad
1001 if (nodeType == null) {
1002 //log something here about what was found and that it was removed
1003 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1004 if (removeDamaged) {
1005 this.deleteWithTraversal(v);
1007 shortCircuit = true;
1010 obj = loader.introspectorFromName(nodeType);
1011 } catch (AAIUnknownObjectException e) {
1012 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1018 //can't make a valid path because we don't understand this object
1021 this.simpleDbToObject(obj, v);
1026 //stop processing and don't return anything for this bad vertex
1031 return new Pair<>(results.get(results.size()-1), objs);
1034 * Takes a list of vertices and a list of objs and assumes they are in
1035 * the order you want the URIs to be nested.
1036 * [A,B,C] creates uris [A, AB, ABC]
1039 * @throws UnsupportedEncodingException
1040 * @throws URISyntaxException
1042 public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1044 String uriChain = "";
1045 for (int i = 0; i < vertices.size(); i++) {
1048 v = vertices.get(i);
1049 aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1050 if (aaiUri != null) {
1053 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1054 aaiUri = uri.toString();
1056 v.property(AAIProperties.AAI_URI, uriChain);
1067 * @throws AAIUnknownObjectException
1068 * @throws IllegalArgumentException elated to property.
1070 * @param relationship the relationship
1071 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1073 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1074 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1075 List<Introspector> relatedToProperties = new ArrayList<>();
1077 if (nameProps != null) {
1078 String[] props = nameProps.split(",");
1079 for (String prop : props) {
1080 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1081 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1082 relatedTo.setValue("property-value", child.getValue(prop));
1083 relatedToProperties.add(relatedTo);
1087 if (relatedToProperties.size() > 0) {
1088 List relatedToList = (List)relationship.getValue("related-to-property");
1089 for (Introspector obj : relatedToProperties) {
1090 relatedToList.add(obj.getUnderlyingObject());
1099 * @param relationship the relationship
1100 * @param inputVertex the input vertex
1101 * @return true, if successful
1102 * @throws UnsupportedEncodingException the unsupported encoding exception
1103 * @throws AAIException the AAI exception
1105 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1107 Vertex relatedVertex = null;
1109 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1111 List<Vertex> results = parser.getQueryBuilder().toList();
1112 if (results.size() == 0) {
1113 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1114 e.getTemplateVars().add(parser.getResultType());
1115 e.getTemplateVars().add(parser.getUri().toString());
1118 //still an issue if there's more than one
1119 relatedVertex = results.get(0);
1122 if (relatedVertex != null) {
1126 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1128 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex);
1131 //attempted to link two vertexes already linked
1133 } catch (NoEdgeRuleFoundException e1) {
1134 throw new AAIException("AAI_6129", e1);
1146 * Gets the edges between.
1148 * @param aVertex the out vertex
1149 * @param bVertex the in vertex
1150 * @return the edges between
1151 * @throws AAIException the AAI exception
1152 * @throws NoEdgeRuleFoundException
1154 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException {
1156 List<Edge> result = new ArrayList<>();
1158 if (bVertex != null) {
1159 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex);
1160 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1161 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id()));
1162 List<Edge> edges = findEdgesBetween.toList();
1163 for (Edge edge : edges) {
1164 if (edge.label().equals(rule.getLabel())) {
1175 * Gets the edge between.
1177 * @param aVertex the out vertex
1178 * @param bVertex the in vertex
1179 * @return the edge between
1180 * @throws AAIException the AAI exception
1181 * @throws NoEdgeRuleFoundException
1183 private Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1187 if (bVertex != null) {
1189 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1191 if (edges.size() > 0) {
1192 return edges.get(0);
1204 * @param relationship the relationship
1205 * @param inputVertex the input vertex
1206 * @return true, if successful
1207 * @throws UnsupportedEncodingException the unsupported encoding exception
1208 * @throws AAIException the AAI exception
1210 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1212 Vertex relatedVertex = null;
1214 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1216 List<Vertex> results = parser.getQueryBuilder().toList();
1218 if (results.size() == 0) {
1222 relatedVertex = results.get(0);
1225 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1226 } catch (NoEdgeRuleFoundException e) {
1227 throw new AAIException("AAI_6129", e);
1239 * Delete items with traversal.
1241 * @param vertexes the vertexes
1242 * @throws IllegalStateException the illegal state exception
1244 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1245 for (Vertex v : vertexes) {
1246 LOGGER.debug("About to delete the vertex with id: " + v.id());
1247 deleteWithTraversal(v);
1252 * Delete with traversal.
1254 * @param startVertex the start vertex
1256 public void deleteWithTraversal(Vertex startVertex) {
1258 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1260 for (Vertex v : results) {
1261 LOGGER.warn("Removing vertex " + v.id().toString());
1272 * @param resourceVersion the resource version
1273 * @throws IllegalArgumentException the illegal argument exception
1274 * @throws AAIException the AAI exception
1275 * @throws InterruptedException the interrupted exception
1277 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1279 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1282 deleteWithTraversal(v);
1283 } catch (IllegalStateException e) {
1284 throw new AAIException("AAI_6110", e);
1293 * Verify delete semantics.
1295 * @param vertex the vertex
1296 * @param resourceVersion the resource version
1297 * @return true, if successful
1298 * @throws AAIException the AAI exception
1300 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1301 boolean result = false;
1302 String nodeType = "";
1303 DeleteSemantic semantic = null;
1304 List<Edge> inEdges = null;
1305 List<Edge> outEdges = null;
1306 String errorDetail = " unknown delete semantic found";
1307 String aaiExceptionCode = "";
1308 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1309 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1311 semantic = edgeRules.getDeleteSemantic(nodeType);
1312 inEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.IN));
1313 outEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.OUT));
1314 if (semantic.equals(DeleteSemantic.CASCADE_TO_CHILDREN)) {
1316 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_EDGES)) {
1317 if (inEdges.isEmpty() && outEdges.isEmpty()) {
1320 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1321 aaiExceptionCode = "AAI_6110";
1323 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_IN_EDGES) || semantic.equals(DeleteSemantic.ERROR_4_IN_EDGES_OR_CASCADE)) {
1325 if (inEdges.isEmpty()) {
1328 //are there any cousin edges?
1330 for (Edge e : inEdges) {
1331 if (e.<Boolean>property("isParent").orElse(false)) {
1336 result = children == inEdges.size();
1340 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1341 aaiExceptionCode = "AAI_6110";
1343 } else if (semantic.equals(DeleteSemantic.THIS_NODE_ONLY)) {
1344 if (outEdges.isEmpty() && inEdges.isEmpty()) {
1348 for (Edge edge : outEdges) {
1349 Object property = edge.<Boolean>property("isParent").orElse(null);
1350 if (property != null && property.equals(Boolean.TRUE)) {
1351 Vertex v = edge.inVertex();
1352 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1353 errorDetail = " Node cannot be deleted using scope = " + semantic +
1354 " another node (type = " + vType + ") depends on it for uniqueness.";
1355 aaiExceptionCode = "AAI_6110";
1361 for (Edge edge : inEdges) {
1362 Object property = edge.<Boolean>property("isParent-REV").orElse(null);
1363 if (property != null && property.equals(Boolean.TRUE)) {
1364 Vertex v = edge.outVertex();
1365 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1366 errorDetail = " Node cannot be deleted using scope = " + semantic +
1367 " another node (type = " + vType + ") depends on it for uniqueness.";
1368 aaiExceptionCode = "AAI_6110";
1378 throw new AAIException(aaiExceptionCode, errorDetail);
1384 * Verify resource version.
1386 * @param action the action
1387 * @param nodeType the node type
1388 * @param currentResourceVersion the current resource version
1389 * @param resourceVersion the resource version
1390 * @param uri the uri
1391 * @return true, if successful
1392 * @throws AAIException the AAI exception
1394 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1395 String enabled = "";
1396 String errorDetail = "";
1397 String aaiExceptionCode = "";
1398 if (currentResourceVersion == null) {
1399 currentResourceVersion = "";
1402 if (resourceVersion == null) {
1403 resourceVersion = "";
1406 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1407 } catch (AAIException e) {
1408 ErrorLogHelper.logException(e);
1410 // We're only doing the resource version checks for v5 and later
1411 if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) {
1412 if (!currentResourceVersion.equals(resourceVersion)) {
1413 if (action.equals("create") && !resourceVersion.equals("")) {
1414 errorDetail = "resource-version passed for " + action + " of " + uri;
1415 aaiExceptionCode = "AAI_6135";
1416 } else if (resourceVersion.equals("")) {
1417 errorDetail = "resource-version not passed for " + action + " of " + uri;
1418 aaiExceptionCode = "AAI_6130";
1420 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1421 aaiExceptionCode = "AAI_6131";
1424 throw new AAIException(aaiExceptionCode, errorDetail);
1432 * Convert from camel case.
1434 * @param name the name
1435 * @return the string
1437 private String convertFromCamelCase (String name) {
1439 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1441 NamingExceptions exceptions = NamingExceptions.getInstance();
1442 result = exceptions.getDBName(result);
1447 private boolean canModify(Introspector obj, String propName, String requestContext) {
1448 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1449 if (readOnly != null) {
1450 final String[] items = readOnly.split(",");
1451 for (String item : items) {
1452 if (requestContext.equals(item)) {
1460 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1462 SideEffectRunner runner = new SideEffectRunner
1463 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1465 runner.execute(obj, self);
1468 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1470 SideEffectRunner runner = new SideEffectRunner
1471 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1473 runner.execute(obj, self);
1476 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1478 SideEffectRunner runner = new SideEffectRunner
1479 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1481 runner.execute(obj, self);