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());
924 if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){
925 this.addRelatedToProperty(result, list.get(0));
928 return result.getUnderlyingObject();
932 * Gets the URI for vertex.
935 * @return the URI for vertex
936 * @throws InstantiationException the instantiation exception
937 * @throws IllegalAccessException the illegal access exception
938 * @throws IllegalArgumentException the illegal argument exception
939 * @throws InvocationTargetException the invocation target exception
940 * @throws NoSuchMethodException the no such method exception
941 * @throws SecurityException the security exception
942 * @throws UnsupportedEncodingException the unsupported encoding exception
943 * @throws AAIUnknownObjectException
945 public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException {
947 return getURIForVertex(v, false);
950 public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException {
953 String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
955 if (aaiUri != null && !overwrite) {
956 uri = UriBuilder.fromPath(aaiUri).build();
958 Pair<Vertex, List<Introspector>> tuple = this.getParents(this.loader, v, false);
959 List<Introspector> list = tuple.getValue1();
960 uri = this.getURIFromList(list);
965 * Gets the URI from list.
967 * @param list the list
968 * @return the URI from list
969 * @throws UnsupportedEncodingException the unsupported encoding exception
971 private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException {
973 StringBuilder sb = new StringBuilder();
974 for (Introspector i : list) {
975 sb.insert(0, i.getURI());
979 URI result = UriBuilder.fromPath(uri).build();
986 * @param start the start
987 * @param removeDamaged the remove damaged
988 * @return the parents
989 * @throws InstantiationException the instantiation exception
990 * @throws IllegalAccessException the illegal access exception
991 * @throws IllegalArgumentException the illegal argument exception
992 * @throws InvocationTargetException the invocation target exception
993 * @throws NoSuchMethodException the no such method exception
994 * @throws SecurityException the security exception
995 * @throws AAIUnknownObjectException
997 private Pair<Vertex, List<Introspector>> getParents(Loader loader, Vertex start, boolean removeDamaged) {
999 List<Vertex> results = this.engine.getQueryEngine().findParents(start);
1000 List<Introspector> objs = new ArrayList<>();
1001 boolean shortCircuit = false;
1002 for (Vertex v : results) {
1003 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1004 Introspector obj = null;
1005 //vertex on the other end of this edge is bad
1006 if (nodeType == null) {
1007 //log something here about what was found and that it was removed
1008 ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString());
1009 if (removeDamaged) {
1010 this.deleteWithTraversal(v);
1012 shortCircuit = true;
1015 obj = loader.introspectorFromName(nodeType);
1016 } catch (AAIUnknownObjectException e) {
1017 LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion());
1023 //can't make a valid path because we don't understand this object
1026 this.simpleDbToObject(obj, v);
1031 //stop processing and don't return anything for this bad vertex
1036 return new Pair<>(results.get(results.size()-1), objs);
1039 * Takes a list of vertices and a list of objs and assumes they are in
1040 * the order you want the URIs to be nested.
1041 * [A,B,C] creates uris [A, AB, ABC]
1044 * @throws UnsupportedEncodingException
1045 * @throws URISyntaxException
1047 public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException {
1049 String uriChain = "";
1050 for (int i = 0; i < vertices.size(); i++) {
1053 v = vertices.get(i);
1054 aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null);
1055 if (aaiUri != null) {
1058 URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build();
1059 aaiUri = uri.toString();
1061 v.property(AAIProperties.AAI_URI, uriChain);
1072 * @throws AAIUnknownObjectException
1073 * @throws IllegalArgumentException elated to property.
1075 * @param relationship the relationship
1076 * @param child the throws IllegalArgumentException, AAIUnknownObjectException child
1078 public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException {
1079 String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS);
1080 List<Introspector> relatedToProperties = new ArrayList<>();
1082 if (nameProps != null) {
1083 String[] props = nameProps.split(",");
1084 for (String prop : props) {
1085 Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property");
1086 relatedTo.setValue("property-key", child.getDbName() + "." + prop);
1087 relatedTo.setValue("property-value", child.getValue(prop));
1088 relatedToProperties.add(relatedTo);
1092 if (relatedToProperties.size() > 0) {
1093 List relatedToList = (List)relationship.getValue("related-to-property");
1094 for (Introspector obj : relatedToProperties) {
1095 relatedToList.add(obj.getUnderlyingObject());
1104 * @param relationship the relationship
1105 * @param inputVertex the input vertex
1106 * @return true, if successful
1107 * @throws UnsupportedEncodingException the unsupported encoding exception
1108 * @throws AAIException the AAI exception
1110 public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1112 Vertex relatedVertex = null;
1114 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1116 List<Vertex> results = parser.getQueryBuilder().toList();
1117 if (results.size() == 0) {
1118 AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri());
1119 e.getTemplateVars().add(parser.getResultType());
1120 e.getTemplateVars().add(parser.getUri().toString());
1123 //still an issue if there's more than one
1124 relatedVertex = results.get(0);
1127 if (relatedVertex != null) {
1131 e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1133 edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex);
1136 //attempted to link two vertexes already linked
1138 } catch (NoEdgeRuleFoundException e1) {
1139 throw new AAIException("AAI_6129", e1);
1151 * Gets the edges between.
1153 * @param aVertex the out vertex
1154 * @param bVertex the in vertex
1155 * @return the edges between
1156 * @throws AAIException the AAI exception
1157 * @throws NoEdgeRuleFoundException
1159 private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException {
1161 List<Edge> result = new ArrayList<>();
1163 if (bVertex != null) {
1164 EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex);
1165 GraphTraversal<Vertex, Edge> findEdgesBetween = null;
1166 findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id()));
1167 List<Edge> edges = findEdgesBetween.toList();
1168 for (Edge edge : edges) {
1169 if (edge.label().equals(rule.getLabel())) {
1180 * Gets the edge between.
1182 * @param aVertex the out vertex
1183 * @param bVertex the in vertex
1184 * @return the edge between
1185 * @throws AAIException the AAI exception
1186 * @throws NoEdgeRuleFoundException
1188 private Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
1192 if (bVertex != null) {
1194 List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex);
1196 if (edges.size() > 0) {
1197 return edges.get(0);
1209 * @param relationship the relationship
1210 * @param inputVertex the input vertex
1211 * @return true, if successful
1212 * @throws UnsupportedEncodingException the unsupported encoding exception
1213 * @throws AAIException the AAI exception
1215 public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException {
1217 Vertex relatedVertex = null;
1219 QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship);
1221 List<Vertex> results = parser.getQueryBuilder().toList();
1223 if (results.size() == 0) {
1227 relatedVertex = results.get(0);
1230 edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex);
1231 } catch (NoEdgeRuleFoundException e) {
1232 throw new AAIException("AAI_6129", e);
1244 * Delete items with traversal.
1246 * @param vertexes the vertexes
1247 * @throws IllegalStateException the illegal state exception
1249 public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException {
1250 for (Vertex v : vertexes) {
1251 LOGGER.debug("About to delete the vertex with id: " + v.id());
1252 deleteWithTraversal(v);
1257 * Delete with traversal.
1259 * @param startVertex the start vertex
1261 public void deleteWithTraversal(Vertex startVertex) {
1263 List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex);
1265 for (Vertex v : results) {
1266 LOGGER.warn("Removing vertex " + v.id().toString());
1277 * @param resourceVersion the resource version
1278 * @throws IllegalArgumentException the illegal argument exception
1279 * @throws AAIException the AAI exception
1280 * @throws InterruptedException the interrupted exception
1282 public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException {
1284 boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion);
1287 deleteWithTraversal(v);
1288 } catch (IllegalStateException e) {
1289 throw new AAIException("AAI_6110", e);
1298 * Verify delete semantics.
1300 * @param vertex the vertex
1301 * @param resourceVersion the resource version
1302 * @return true, if successful
1303 * @throws AAIException the AAI exception
1305 private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException {
1306 boolean result = false;
1307 String nodeType = "";
1308 DeleteSemantic semantic = null;
1309 List<Edge> inEdges = null;
1310 List<Edge> outEdges = null;
1311 String errorDetail = " unknown delete semantic found";
1312 String aaiExceptionCode = "";
1313 nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1314 if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) {
1316 semantic = edgeRules.getDeleteSemantic(nodeType);
1317 inEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.IN));
1318 outEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.OUT));
1319 if (semantic.equals(DeleteSemantic.CASCADE_TO_CHILDREN)) {
1321 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_EDGES)) {
1322 if (inEdges.isEmpty() && outEdges.isEmpty()) {
1325 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1326 aaiExceptionCode = "AAI_6110";
1328 } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_IN_EDGES) || semantic.equals(DeleteSemantic.ERROR_4_IN_EDGES_OR_CASCADE)) {
1330 if (inEdges.isEmpty()) {
1333 //are there any cousin edges?
1335 for (Edge e : inEdges) {
1336 if (e.<Boolean>property("isParent").orElse(false)) {
1341 result = children == inEdges.size();
1345 errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n";
1346 aaiExceptionCode = "AAI_6110";
1348 } else if (semantic.equals(DeleteSemantic.THIS_NODE_ONLY)) {
1349 if (outEdges.isEmpty() && inEdges.isEmpty()) {
1353 for (Edge edge : outEdges) {
1354 Object property = edge.<Boolean>property("isParent").orElse(null);
1355 if (property != null && property.equals(Boolean.TRUE)) {
1356 Vertex v = edge.inVertex();
1357 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1358 errorDetail = " Node cannot be deleted using scope = " + semantic +
1359 " another node (type = " + vType + ") depends on it for uniqueness.";
1360 aaiExceptionCode = "AAI_6110";
1366 for (Edge edge : inEdges) {
1367 Object property = edge.<Boolean>property("isParent-REV").orElse(null);
1368 if (property != null && property.equals(Boolean.TRUE)) {
1369 Vertex v = edge.outVertex();
1370 String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1371 errorDetail = " Node cannot be deleted using scope = " + semantic +
1372 " another node (type = " + vType + ") depends on it for uniqueness.";
1373 aaiExceptionCode = "AAI_6110";
1383 throw new AAIException(aaiExceptionCode, errorDetail);
1389 * Verify resource version.
1391 * @param action the action
1392 * @param nodeType the node type
1393 * @param currentResourceVersion the current resource version
1394 * @param resourceVersion the resource version
1395 * @param uri the uri
1396 * @return true, if successful
1397 * @throws AAIException the AAI exception
1399 public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException {
1400 String enabled = "";
1401 String errorDetail = "";
1402 String aaiExceptionCode = "";
1403 if (currentResourceVersion == null) {
1404 currentResourceVersion = "";
1407 if (resourceVersion == null) {
1408 resourceVersion = "";
1411 enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
1412 } catch (AAIException e) {
1413 ErrorLogHelper.logException(e);
1415 // We're only doing the resource version checks for v5 and later
1416 if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) {
1417 if (!currentResourceVersion.equals(resourceVersion)) {
1418 if (action.equals("create") && !resourceVersion.equals("")) {
1419 errorDetail = "resource-version passed for " + action + " of " + uri;
1420 aaiExceptionCode = "AAI_6135";
1421 } else if (resourceVersion.equals("")) {
1422 errorDetail = "resource-version not passed for " + action + " of " + uri;
1423 aaiExceptionCode = "AAI_6130";
1425 errorDetail = "resource-version MISMATCH for " + action + " of " + uri;
1426 aaiExceptionCode = "AAI_6131";
1429 throw new AAIException(aaiExceptionCode, errorDetail);
1437 * Convert from camel case.
1439 * @param name the name
1440 * @return the string
1442 private String convertFromCamelCase (String name) {
1444 result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
1446 NamingExceptions exceptions = NamingExceptions.getInstance();
1447 result = exceptions.getDBName(result);
1452 private boolean canModify(Introspector obj, String propName, String requestContext) {
1453 final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY);
1454 if (readOnly != null) {
1455 final String[] items = readOnly.split(",");
1456 for (String item : items) {
1457 if (requestContext.equals(item)) {
1465 private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException {
1467 SideEffectRunner runner = new SideEffectRunner
1468 .Builder(this.engine, this).addSideEffect(DataCopy.class).build();
1470 runner.execute(obj, self);
1473 private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException {
1475 SideEffectRunner runner = new SideEffectRunner
1476 .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build();
1478 runner.execute(obj, self);
1481 private void enrichData(Introspector obj, Vertex self) throws AAIException {
1483 SideEffectRunner runner = new SideEffectRunner
1484 .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build();
1486 runner.execute(obj, self);