diff options
Diffstat (limited to 'aai-core/src/main/java/org/openecomp/aai/serialization')
32 files changed, 4292 insertions, 0 deletions
diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/DBSerializer.java new file mode 100644 index 00000000..fb3300e3 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/DBSerializer.java @@ -0,0 +1,1484 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.base.CaseFormat; +import com.thinkaurelius.titan.core.SchemaViolationException; +import org.apache.commons.collections.IteratorUtils; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.*; +import org.javatuples.Pair; +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.*; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.introspection.sideeffect.DataCopy; +import org.openecomp.aai.introspection.sideeffect.DataLinkReader; +import org.openecomp.aai.introspection.sideeffect.DataLinkWriter; +import org.openecomp.aai.introspection.sideeffect.SideEffectRunner; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.parsers.query.QueryParser; +import org.openecomp.aai.parsers.uri.URIParser; +import org.openecomp.aai.parsers.uri.URIToRelationshipObject; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.schema.enums.ObjectMetadata; +import org.openecomp.aai.schema.enums.PropertyMetadata; +import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.tinkerpop.TreeBackedVertex; +import org.openecomp.aai.util.AAIApiServerURLBase; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import org.openecomp.aai.workarounds.NamingExceptions; + +import javax.ws.rs.core.UriBuilder; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +public class DBSerializer { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); + + private final TransactionalGraphEngine engine; + private final String sourceOfTruth; + private final ModelType introspectionType; + private final Version version; + private final Loader latestLoader; + private final EdgeRules edgeRules = EdgeRules.getInstance(); + private final Loader loader; + private final String baseURL; + /** + * Instantiates a new DB serializer. + * + * @param version the version + * @param engine the engine + * @param g the g + * @param introspectionType the introspection type + * @param sourceOfTruth the source of truth + * @param llBuilder the ll builder + * @throws AAIException + */ + public DBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { + this.engine = engine; + this.sourceOfTruth = sourceOfTruth; + this.introspectionType = introspectionType; + this.latestLoader = LoaderFactory.createLoaderForVersion(introspectionType, AAIProperties.LATEST); + this.version = version; + this.loader = LoaderFactory.createLoaderForVersion(introspectionType, version); + this.baseURL = AAIApiServerURLBase.get(version); + } + + /** + * Touch standard vertex properties. + * + * @param v the v + * @param isNewVertex the is new vertex + */ + /* + * to be defined and expanded later + */ + public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { + + long unixTimeNow = System.currentTimeMillis(); + String timeNowInSec = "" + unixTimeNow; + if (isNewVertex) { + v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); + v.property(AAIProperties.CREATED_TS, timeNowInSec); + } + v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec ); + v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); + v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); + + } + + + + /** + * Creates the new vertex. + * + * @param wrappedObject the wrapped object + * @return the vertex + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public Vertex createNewVertex(Introspector wrappedObject) { + + + Vertex v = this.engine.tx().addVertex(); + v.property(AAIProperties.NODE_TYPE, wrappedObject.getDbName()); + touchStandardVertexProperties(v, true); + + return v; + } + + /** + * Trim class name. + * + * @param className the class name + * @return the string + */ + /* + * Removes the classpath from a class name + */ + public String trimClassName (String className) { + String returnValue = ""; + + if (className.lastIndexOf('.') == -1) { + return className; + } + returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); + + return returnValue; + } + + /** + * Serialize to db. + * + * @param obj the obj + * @param v the v + * @param uriQuery the uri query + * @param identifier the identifier + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws InterruptedException the interrupted exception + * @throws NoSuchMethodException the no such method exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { + + try { + if (uriQuery.isDependent()) { + //try to find the parent + List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList(); + if (!vertices.isEmpty()) { + Vertex parent = vertices.get(0); + this.reflectDependentVertex(parent, v, obj, requestContext); + } else { + throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier); + } + } else { + serializeSingleVertex(v, obj, requestContext); + } + + } catch (SchemaViolationException e) { + throw new AAIException("AAI_6117", e); + } + + } + + public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException { + try { + boolean isTopLevel = obj.isTopLevel(); + if (isTopLevel) { + v.property(AAIProperties.AAI_URI, obj.getURI()); + } + processObject(obj, v, requestContext); + if (!isTopLevel) { + URI uri = this.getURIForVertex(v); + URIParser parser = new URIParser(this.loader, uri); + if (parser.validate()) { + VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); + if (!uriProp.isPresent() || uriProp.isPresent() && !uriProp.value().equals(uri.toString())) { + v.property(AAIProperties.AAI_URI, uri.toString()); + } + } + } + } catch (SchemaViolationException e) { + throw new AAIException("AAI_6117", e); + } + } + + /** + * Process object. + * + * @param <T> the generic type + * @param obj the obj + * @param v the v + * @return the list + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + /* + * Helper method for reflectToDb + * Handles all the property setting + */ + private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { + Set<String> properties = new LinkedHashSet<>(obj.getProperties()); + properties.remove(AAIProperties.RESOURCE_VERSION); + List<Vertex> dependentVertexes = new ArrayList<>(); + List<Vertex> processedVertexes = new ArrayList<>(); + boolean isComplexType = false; + boolean isListType = false; + this.executePreSideEffects(obj, v); + for (String property : properties) { + Object value = null; + final String propertyType; + propertyType = obj.getType(property); + isComplexType = obj.isComplexType(property); + isListType = obj.isListType(property); + value = obj.getValue(property); + + if (!(isComplexType || isListType)) { + boolean canModify = this.canModify(obj, property, requestContext); + + if (canModify) { + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbProperty = property; + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbProperty = metadata.get(PropertyMetadata.DB_ALIAS); + } + if (metadata.containsKey(PropertyMetadata.DATA_LINK)) { + //data linked properties are ephemeral + //they are populated dynamically on GETs + continue; + } + if (value != null) { + if (propertyType.toLowerCase().contains(".long")) { + v.property(dbProperty, new Integer(((Long)value).toString())); + } else { + v.property(dbProperty, value); + } + } else { + v.property(dbProperty).remove(); + } + } + } else if (isListType) { + List<Object> list = (List<Object>)value; + if (obj.isComplexGenericType(property)) { + if (list != null) { + for (Object o : list) { + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + } + } + } else { + //simple list case + engine.setListProperty(v, property, list); + } + } else { + //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method + if (value != null) { //effectively ignore complex properties not included in the object we're processing + if (value.getClass().isArray()) { + + int length = Array.getLength(value); + for (int i = 0; i < length; i ++) { + Object arrayElement = Array.get(value, i); + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + + } + } else if (!property.equals("relationship-list")) { + // container case + Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value); + if (introspector.isContainer()) { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName())); + introspector.setURIChain(obj.getURI()); + + processedVertexes.addAll(processObject(introspector, v, requestContext)); + + } else { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName())); + processedVertexes.add(reflectDependentVertex(v, introspector, requestContext)); + + } + } else if (property.equals("relationship-list")) { + handleRelationships(obj, v); + } + } + } + } + this.writeThroughDefaults(v, obj); + /* handle those vertexes not touched */ + for (Vertex toBeRemoved : processedVertexes) { + dependentVertexes.remove(toBeRemoved); + } + this.deleteItemsWithTraversal(dependentVertexes); + + this.executePostSideEffects(obj, v); + return processedVertexes; + } + + /** + * Handle relationships. + * + * @param obj the obj + * @param vertex the vertex + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + /* + * Handles the explicit relationships defined for an obj + */ + private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException { + + + + Introspector wrappedRl = obj.getWrappedValue("relationship-list"); + processRelationshipList(wrappedRl, vertex); + + + } + + + /** + * Process relationship list. + * + * @param wrapped the wrapped + * @param v the v + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException { + + List<Object> relationships = (List<Object>)wrapped.getValue("relationship"); + + List<Pair<Vertex, Vertex>> addEdges = new ArrayList<>(); + List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); + + for (Object relationship : relationships) { + Edge e = null; + Vertex cousinVertex = null; + Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + ex.getTemplateVars().add(parser.getResultType()); + ex.getTemplateVars().add(parser.getUri().toString()); + throw ex; + } else { + //still an issue if there's more than one + cousinVertex = results.get(0); + } + + if (cousinVertex != null) { + try { + e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex); + + if (e == null) { + addEdges.add(new Pair<>(v, cousinVertex)); + } else { + existingEdges.remove(e); + } + } catch (NoEdgeRuleFoundException e1) { + throw new AAIException("AAI_6145"); + } + } + } + + for (Edge edge : existingEdges) { + edge.remove(); + } + for (Pair<Vertex, Vertex> pair : addEdges) { + try { + edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1()); + } catch (NoEdgeRuleFoundException e) { + throw new AAIException("AAI_6129", e); + } + } + + } + + /** + * Write through defaults. + * + * @param v the v + * @param obj the obj + * @throws AAIUnknownObjectException + */ + private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException { + Introspector latest = this.latestLoader.introspectorFromName(obj.getName()); + if (latest != null) { + Set<String> required = latest.getRequiredProperties(); + + for (String field : required) { + String defaultValue = null; + Object vertexProp = null; + defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); + if (defaultValue != null) { + vertexProp = v.<Object>property(field).orElse(null); + if (vertexProp == null) { + v.property(field, defaultValue); + } + } + } + } + + } + + + /** + * Reflect dependent vertex. + * + * @param v the v + * @param dependentObj the dependent obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { + + //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); + //List<Vertex> items = p.getQuery().toList(); + QueryBuilder query = this.engine.getQueryBuilder(v); + query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); + query.createKeyQuery(dependentObj); + + List<Vertex> items = query.toList(); + + Vertex dependentVertex = null; + if (items.size() == 1) { + dependentVertex = items.get(0); + this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); + } else { + this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); + dependentVertex = createNewVertex(dependentObj); + } + + return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext); + + } + + /** + * Reflect dependent vertex. + * + * @param parent the parent + * @param child the child + * @param obj the obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException { + + String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null); + if (parentUri != null) { + child.property(AAIProperties.AAI_URI, parentUri + obj.getURI()); + } + processObject(obj, child, requestContext); + + Edge e; + e = this.getEdgeBetween(EdgeType.TREE, parent, child); + if (e == null) { + String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); + if (canBeLinked != null && canBeLinked.equals("true")) { + boolean isFirst = !this.engine.getQueryBuilder(parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext(); + if (isFirst) { + child.property(AAIProperties.LINKED, true); + } + } + edgeRules.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); + } + return child; + + } + + /** + * Db to object. + * + * @param vertices the vertices + * @param obj the obj + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException { + + final int internalDepth; + if (depth == Integer.MAX_VALUE) { + internalDepth = depth--; + } else { + internalDepth = depth; + } + if (vertices.size() > 1 && !obj.isContainer()) { + throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); + } else if (obj.isContainer()) { + final List getList; + String listProperty = null; + for (String property : obj.getProperties()) { + if (obj.isListType(property) && obj.isComplexGenericType(property)) { + listProperty = property; + break; + } + } + final String propertyName = listProperty; + getList = (List)obj.getValue(listProperty); + + /* This is an experimental multithreading experiment + * on get alls. + */ + ExecutorService pool = GetAllPool.getInstance().getPool(); + + List<Future<Object>> futures = new ArrayList<>(); + for (Vertex v : vertices) { + Callable<Object> task = () -> { + Set<Vertex> seen = new HashSet<>(); + Introspector childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); + Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, internalDepth, nodeOnly); + TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); + dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp); + return childObject.getUnderlyingObject(); + //getList.add(childObject.getUnderlyingObject()); + }; + futures.add(pool.submit(task)); + } + + for (Future<Object> future : futures) { + try { + getList.add(future.get()); + } catch (ExecutionException e) { + throw new AAIException("AAI_4000", e); + } catch (InterruptedException e) { + throw new AAIException("AAI_4000", e); + } + } + } else if (vertices.size() == 1) { + Set<Vertex> seen = new HashSet<>(); + Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly); + TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree); + dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); + } else { + //obj = null; + } + + + return obj; + } + + /** + * Db to object. + * + * @param obj the obj + * @param v the v + * @param seen the seen + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { + + if (depth < 0) { + return null; + } + depth--; + seen.add(v); + + boolean modified = false; + for (String property : obj.getProperties(PropertyPredicates.isVisible())) { + List<Object> getList = null; + Vertex[] vertices = null; + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + modified = true; + } else { + if (obj.isComplexType(property)) { + /* container case */ + + if (!property.equals("relationship-list") && depth >= 0) { + Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); + Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp); + if (result != null) { + obj.setValue(property, argumentObject.getUnderlyingObject()); + modified = true; + } + } else if (property.equals("relationship-list") && !nodeOnly){ + /* relationships need to be handled correctly */ + Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); + relationshipList = createRelationshipList(v, relationshipList, cleanUp); + if (relationshipList != null) { + modified = true; + obj.setValue(property, relationshipList.getUnderlyingObject()); + modified = true; + } + + } + } else if (obj.isListType(property)) { + + if (property.equals("any")) { + continue; + } + String genericType = obj.getGenericTypeClass(property).getSimpleName(); + if (obj.isComplexGenericType(property) && depth >= 0) { + final String childDbName = convertFromCamelCase(genericType); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + EdgeRule rule; + + rule = edgeRules.getEdgeRule(EdgeType.TREE, vType, childDbName); + if (rule.getIsParent().equals("true") || rule.getIsParent().equals("reverse")) { + //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); + Direction ruleDirection = rule.getDirection(); + Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); + List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); + itr = verticesList.stream().filter(item -> { + return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); + }).iterator(); + if (itr.hasNext()) { + getList = (List<Object>)obj.getValue(property); + } + int processed = 0; + int removed = 0; + while (itr.hasNext()) { + Vertex childVertex = itr.next(); + if (!seen.contains(childVertex)) { + Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); + + Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); + if (result != null) { + getList.add(argumentObject.getUnderlyingObject()); + } + + processed++; + } else { + removed++; + LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); + } + } + if (processed == 0) { + //vertices were all seen, reset the list + getList = null; + } + if (processed > 0) { + modified = true; + } + } + } else if (obj.isSimpleGenericType(property)) { + List<Object> temp = this.engine.getListProperty(v, property); + if (temp != null) { + getList = (List<Object>)obj.getValue(property); + getList.addAll(temp); + modified = true; + } + + } + + } + + } + } + + //no changes were made to this obj, discard the instance + if (!modified) { + return null; + } + this.enrichData(obj, v); + return obj; + + } + + + public Introspector getVertexProperties(Vertex v) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = 0; + String cleanUp = "false"; + boolean nodeOnly = true; + this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); + + return obj; + + } + public Introspector getLatestVersionView(Vertex v) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = AAIProperties.MAXIMUM_DEPTH; + String cleanUp = "false"; + boolean nodeOnly = false; + Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly); + TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); + this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); + + return obj; + } + /** + * Copy simple property. + * + * @param property the property + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void copySimpleProperty(String property, Introspector obj, Vertex v) { + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbPropertyName = property; + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); + } + final Object temp = v.<Object>property(dbPropertyName).orElse(null); + if (temp != null) { + + obj.setValue(property, temp); + } + } + + /** + * Simple db to object. + * + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void simpleDbToObject (Introspector obj, Vertex v) { + for (String property : obj.getProperties()) { + + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + } + } + } + + /** + * Creates the relationship list. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws URISyntaxException + */ + private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { + + + List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v); + + List<Object> relationshipObjList = obj.getValue("relationship"); + + for (Vertex cousin : cousins) { + + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp); + if (result != null) { + relationshipObjList.add(result); + } + + } + + if (relationshipObjList.isEmpty()) { + return null; + } else { + return obj; + } + } + + /** + * Process edge relationship. + * + * @param relationshipObj the relationship obj + * @param edge the edge + * @param direction the direction + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException { + + + //we must look up all parents in this case because we need to compute name-properties + //we cannot used the cached aaiUri to perform this action currently + Pair<Vertex, List<Introspector>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp)); + //damaged vertex found, ignore + if (tuple == null) { + return null; + } + List<Introspector> list = tuple.getValue1(); + URI uri = this.getURIFromList(list); + + URIToRelationshipObject uriParser = null; + Introspector result = null; + try { + uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); + result = uriParser.getResult(); + } catch (AAIException | URISyntaxException e) { + LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.getValue0().id().toString() + ": " + e.getMessage(), e); + if ("true".equals(cleanUp)) { + this.deleteWithTraversal(tuple.getValue0()); + } + return null; + } + return result.getUnderlyingObject(); + } + + /** + * Gets the URI for vertex. + * + * @param v the v + * @return the URI for vertex + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException { + + return getURIForVertex(v, false); + } + + public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { + URI uri = null; + + String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); + + if (aaiUri != null && !overwrite) { + uri = UriBuilder.fromPath(aaiUri).build(); + } else { + Pair<Vertex, List<Introspector>> tuple = this.getParents(this.loader, v, false); + List<Introspector> list = tuple.getValue1(); + uri = this.getURIFromList(list); + } + return uri; + } + /** + * Gets the URI from list. + * + * @param list the list + * @return the URI from list + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { + String uri = ""; + StringBuilder sb = new StringBuilder(); + for (Introspector i : list) { + sb.insert(0, i.getURI()); + } + + uri = sb.toString(); + URI result = UriBuilder.fromPath(uri).build(); + return result; + } + + /** + * Gets the parents. + * + * @param start the start + * @param removeDamaged the remove damaged + * @return the parents + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIUnknownObjectException + */ + private Pair<Vertex, List<Introspector>> getParents(Loader loader, Vertex start, boolean removeDamaged) { + + List<Vertex> results = this.engine.getQueryEngine().findParents(start); + List<Introspector> objs = new ArrayList<>(); + boolean shortCircuit = false; + for (Vertex v : results) { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + Introspector obj = null; + //vertex on the other end of this edge is bad + if (nodeType == null) { + //log something here about what was found and that it was removed + ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString()); + if (removeDamaged) { + this.deleteWithTraversal(v); + } + shortCircuit = true; + } else { + try { + obj = loader.introspectorFromName(nodeType); + } catch (AAIUnknownObjectException e) { + LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion()); + obj = null; + } + } + + if (obj == null) { + //can't make a valid path because we don't understand this object + // don't include it + } else { + this.simpleDbToObject(obj, v); + objs.add(obj); + } + } + + //stop processing and don't return anything for this bad vertex + if (shortCircuit) { + return null; + } + + return new Pair<>(results.get(results.size()-1), objs); + } + /** + * Takes a list of vertices and a list of objs and assumes they are in + * the order you want the URIs to be nested. + * [A,B,C] creates uris [A, AB, ABC] + * @param vertices + * @param objs + * @throws UnsupportedEncodingException + * @throws URISyntaxException + */ + public void setCachedURIs(List<Vertex> vertices, List<Introspector> objs) throws UnsupportedEncodingException, URISyntaxException { + + String uriChain = ""; + for (int i = 0; i < vertices.size(); i++) { + String aaiUri = ""; + Vertex v = null; + v = vertices.get(i); + aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); + if (aaiUri != null) { + uriChain += aaiUri; + } else { + URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build(); + aaiUri = uri.toString(); + uriChain += aaiUri; + v.property(AAIProperties.AAI_URI, uriChain); + } + } + + + + } + + + /** + * Adds the r + * @throws AAIUnknownObjectException + * @throws IllegalArgumentException elated to property. + * + * @param relationship the relationship + * @param child the throws IllegalArgumentException, AAIUnknownObjectException child + */ + public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException { + String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS); + List<Introspector> relatedToProperties = new ArrayList<>(); + + if (nameProps != null) { + String[] props = nameProps.split(","); + for (String prop : props) { + Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property"); + relatedTo.setValue("property-key", child.getDbName() + "." + prop); + relatedTo.setValue("property-value", child.getValue(prop)); + relatedToProperties.add(relatedTo); + } + } + + if (relatedToProperties.size() > 0) { + List relatedToList = (List)relationship.getValue("related-to-property"); + for (Introspector obj : relatedToProperties) { + relatedToList.add(obj.getUnderlyingObject()); + } + } + + } + + /** + * Creates the edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.size() == 0) { + AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + e.getTemplateVars().add(parser.getResultType()); + e.getTemplateVars().add(parser.getUri().toString()); + throw e; + } else { + //still an issue if there's more than one + relatedVertex = results.get(0); + } + + if (relatedVertex != null) { + + Edge e; + try { + e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex); + if (e == null) { + edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex); + + } else { + //attempted to link two vertexes already linked + } + } catch (NoEdgeRuleFoundException e1) { + throw new AAIException("AAI_6129", e1); + } + + + + + } + + return true; + } + + /** + * Gets the edges between. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException { + + List<Edge> result = new ArrayList<>(); + + if (bVertex != null) { + EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex); + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id())); + List<Edge> edges = findEdgesBetween.toList(); + for (Edge edge : edges) { + if (edge.label().equals(rule.getLabel())) { + result.add(edge); + } + } + + } + + return result; + } + + /** + * Gets the edge between. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + + + + if (bVertex != null) { + + List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex); + + if (edges.size() > 0) { + return edges.get(0); + } + + } + + return null; + } + + + /** + * Delete edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + List<Vertex> results = parser.getQueryBuilder().toList(); + + if (results.size() == 0) { + return false; + } + + relatedVertex = results.get(0); + Edge edge; + try { + edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex); + } catch (NoEdgeRuleFoundException e) { + throw new AAIException("AAI_6129", e); + } + if (edge != null) { + edge.remove(); + return true; + } else { + return false; + } + + } + + /** + * Delete items with traversal. + * + * @param vertexes the vertexes + * @throws IllegalStateException the illegal state exception + */ + public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { + for (Vertex v : vertexes) { + LOGGER.debug("About to delete the vertex with id: " + v.id()); + deleteWithTraversal(v); + } + } + + /** + * Delete with traversal. + * + * @param startVertex the start vertex + */ + public void deleteWithTraversal(Vertex startVertex) { + + List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); + + for (Vertex v : results) { + LOGGER.warn("Removing vertex " + v.id().toString()); + + v.remove(); + } + + } + + /** + * Delete. + * + * @param v the v + * @param resourceVersion the resource version + * @throws IllegalArgumentException the illegal argument exception + * @throws AAIException the AAI exception + * @throws InterruptedException the interrupted exception + */ + public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { + + boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); + if (result) { + try { + deleteWithTraversal(v); + } catch (IllegalStateException e) { + throw new AAIException("AAI_6110", e); + } + + } + + } + + + /** + * Verify delete semantics. + * + * @param vertex the vertex + * @param resourceVersion the resource version + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { + boolean result = false; + String nodeType = ""; + DeleteSemantic semantic = null; + List<Edge> inEdges = null; + List<Edge> outEdges = null; + String errorDetail = " unknown delete semantic found"; + String aaiExceptionCode = ""; + nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { + } + semantic = edgeRules.getDeleteSemantic(nodeType); + inEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.IN)); + outEdges = (List<Edge>) IteratorUtils.toList(vertex.edges(Direction.OUT)); + if (semantic.equals(DeleteSemantic.CASCADE_TO_CHILDREN)) { + result = true; + } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_EDGES)) { + if (inEdges.isEmpty() && outEdges.isEmpty()) { + result = true; + } else { + errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n"; + aaiExceptionCode = "AAI_6110"; + } + } else if (semantic.equals(DeleteSemantic.ERROR_IF_ANY_IN_EDGES) || semantic.equals(DeleteSemantic.ERROR_4_IN_EDGES_OR_CASCADE)) { + + if (inEdges.isEmpty()) { + result = true; + } else { + //are there any cousin edges? + long children = 0; + for (Edge e : inEdges) { + if (e.<Boolean>property("isParent").orElse(false)) { + children++; + } + } + + result = children == inEdges.size(); + } + + if (!result) { + errorDetail = " Node cannot be deleted because it still has Edges and the " + semantic + " scope was used.\n"; + aaiExceptionCode = "AAI_6110"; + } + } else if (semantic.equals(DeleteSemantic.THIS_NODE_ONLY)) { + if (outEdges.isEmpty() && inEdges.isEmpty()) { + result = true; + } else { + result = true; + for (Edge edge : outEdges) { + Object property = edge.<Boolean>property("isParent").orElse(null); + if (property != null && property.equals(Boolean.TRUE)) { + Vertex v = edge.inVertex(); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + errorDetail = " Node cannot be deleted using scope = " + semantic + + " another node (type = " + vType + ") depends on it for uniqueness."; + aaiExceptionCode = "AAI_6110"; + result = false; + break; + } + } + + for (Edge edge : inEdges) { + Object property = edge.<Boolean>property("isParent-REV").orElse(null); + if (property != null && property.equals(Boolean.TRUE)) { + Vertex v = edge.outVertex(); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + errorDetail = " Node cannot be deleted using scope = " + semantic + + " another node (type = " + vType + ") depends on it for uniqueness."; + aaiExceptionCode = "AAI_6110"; + result = false; + break; + } + } + } + } + + + if (!result) { + throw new AAIException(aaiExceptionCode, errorDetail); + } + return result; + } + + /** + * Verify resource version. + * + * @param action the action + * @param nodeType the node type + * @param currentResourceVersion the current resource version + * @param resourceVersion the resource version + * @param uri the uri + * @return true, if successful + * @throws AAIException the AAI exception + */ + public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException { + String enabled = ""; + String errorDetail = ""; + String aaiExceptionCode = ""; + if (currentResourceVersion == null) { + currentResourceVersion = ""; + } + + if (resourceVersion == null) { + resourceVersion = ""; + } + try { + enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG); + } catch (AAIException e) { + ErrorLogHelper.logException(e); + } + // We're only doing the resource version checks for v5 and later + if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) { + if (!currentResourceVersion.equals(resourceVersion)) { + if (action.equals("create") && !resourceVersion.equals("")) { + errorDetail = "resource-version passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6135"; + } else if (resourceVersion.equals("")) { + errorDetail = "resource-version not passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6130"; + } else { + errorDetail = "resource-version MISMATCH for " + action + " of " + uri; + aaiExceptionCode = "AAI_6131"; + } + + throw new AAIException(aaiExceptionCode, errorDetail); + + } + } + return true; + } + + /** + * Convert from camel case. + * + * @param name the name + * @return the string + */ + private String convertFromCamelCase (String name) { + String result = ""; + result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); + + NamingExceptions exceptions = NamingExceptions.getInstance(); + result = exceptions.getDBName(result); + + return result; + } + + private boolean canModify(Introspector obj, String propName, String requestContext) { + final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY); + if (readOnly != null) { + final String[] items = readOnly.split(","); + for (String item : items) { + if (requestContext.equals(item)) { + return false; + } + } + } + return true; + } + + private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataCopy.class).build(); + + runner.execute(obj, self); + } + + private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); + + runner.execute(obj, self); + } + + private void enrichData(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); + + runner.execute(obj, self); + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/DeleteSemantic.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/DeleteSemantic.java new file mode 100644 index 00000000..65a39497 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/DeleteSemantic.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + + +/** + * Possible values for deleteScope can be: + * USE_DEFAULT - Get the scope from ref data for this node + * THIS_NODE_ONLY (but should fail if it there are nodes that depend on it for uniqueness) + * CASCADE_TO_CHILDREN - will look for OUT-Edges that have parentOf/hasDelTarget = true and follow those down + * ERROR_4_IN_EDGES_OR_CASCADE - combo of error-if-any-IN-edges + CascadeToChildren + * ERROR_IF_ANY_IN_EDGES - Fail if this node has any existing IN edges + * ERROR_IF_ANY_EDGES - Fail if this node has any existing edges at all! + */ +public enum DeleteSemantic { + USE_DEFAULT, + THIS_NODE_ONLY, + CASCADE_TO_CHILDREN, + ERROR_4_IN_EDGES_OR_CASCADE, + ERROR_IF_ANY_IN_EDGES, + ERROR_IF_ANY_EDGES, +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRule.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRule.java new file mode 100644 index 00000000..d7abfdc0 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRule.java @@ -0,0 +1,202 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.tinkerpop.gremlin.structure.Direction; + +public class EdgeRule { + + private String label = ""; + private MultiplicityRule multiplicityRule = null; + private Direction direction = null; + private Map<String, String> edgeProperties = null; + private final String IS_PARENT = "isParent"; + private final String USES_RESOURCE = "usesResource"; + private final String HAS_DEL_TARGET = "hasDelTarget"; + private final String SVC_INFRA = "SVC-INFRA"; + + /** + * Instantiates a new edge rule. + */ + public EdgeRule() { + edgeProperties = new HashMap<>(); + } + + /** + * Gets the label. + * + * @return the label + */ + public String getLabel() { + return label; + } + + /** + * Sets the label. + * + * @param label the new label + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * Gets the multiplicity rule. + * + * @return the multiplicity rule + */ + public MultiplicityRule getMultiplicityRule() { + return multiplicityRule; + } + + /** + * Sets the multiplicity rule. + * + * @param multiplicityRule the new multiplicity rule + */ + public void setMultiplicityRule(MultiplicityRule multiplicityRule) { + this.multiplicityRule = multiplicityRule; + } + + /** + * Gets the direction. + * + * @return the direction + */ + public Direction getDirection() { + return direction; + } + + /** + * Sets the direction. + * + * @param direction the new direction + */ + public void setDirection(Direction direction) { + this.direction = direction; + } + + /** + * Gets the checks if is parent. + * + * @return the checks if is parent + */ + public String getIsParent() { + return this.getProp(this.IS_PARENT); + } + + /** + * Sets the checks if is parent. + * + * @param isParent the new checks if is parent + */ + public void setIsParent(String isParent) { + this.setProp(this.IS_PARENT, isParent); + } + + /** + * Gets the uses resource. + * + * @return the uses resource + */ + public String getUsesResource() { + return this.getProp(this.USES_RESOURCE); + } + + /** + * Sets the uses resource. + * + * @param usesResource the new uses resource + */ + public void setUsesResource(String usesResource) { + this.setProp(this.USES_RESOURCE, usesResource); + } + + /** + * Gets the checks for del target. + * + * @return the checks for del target + */ + public String getHasDelTarget() { + return this.getProp(this.HAS_DEL_TARGET); + } + + /** + * Sets the checks for del target. + * + * @param hasDelTarget the new checks for del target + */ + public void setHasDelTarget(String hasDelTarget) { + this.setProp(this.HAS_DEL_TARGET, hasDelTarget); + } + + /** + * Gets the service infrastructure. + * + * @return the service infrastructure + */ + public String getServiceInfrastructure() { + return this.getProp(this.SVC_INFRA); + } + + /** + * Sets the service infrastructure. + * + * @param serviceInfrastructure the new service infrastructure + */ + public void setServiceInfrastructure(String serviceInfrastructure) { + this.setProp(this.SVC_INFRA, serviceInfrastructure); + } + + /** + * Gets the edge properties. + * + * @return the edge properties + */ + public Map<String, String> getEdgeProperties() { + return this.edgeProperties; + } + + /** + * Sets the prop. + * + * @param key the key + * @param value the value + */ + private void setProp(String key, String value) { + this.edgeProperties.put(key, value); + } + + /** + * Gets the prop. + * + * @param key the key + * @return the prop + */ + private String getProp(String key) { + return this.edgeProperties.get(key); + } + + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRules.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRules.java new file mode 100644 index 00000000..06dfe564 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeRules.java @@ -0,0 +1,379 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.dbmodel.DbEdgeRules; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +public class EdgeRules { + + private Multimap<String, String> rules = DbEdgeRules.EdgeRules; + private Multimap<String, String> deleteScope = DbEdgeRules.DefaultDeleteScope; + private final int EDGE_NAME = 0; + private final int DIRECTION = 1; + private final int MULTIPLICITY_RULE = 2; + private final int IS_PARENT = 3; + private final int USES_RESOURCE = 4; + private final int HAS_DEL_TARGET = 5; + private final int SVC_INFRA = 6; + + /** + * Instantiates a new edge rules. + */ + private EdgeRules() { + + } + private static class Helper { + private static final EdgeRules INSTANCE = new EdgeRules(); + + } + + /** + * Gets the single instance of EdgeRules. + * + * @return single instance of EdgeRules + */ + public static EdgeRules getInstance() { + return Helper.INSTANCE; + + } + + /** + * Adds the tree edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + public Edge addTreeEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex); + } + + /** + * Adds the edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex); + } + + /** + * Adds the edge. + * + * @param type the type + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException { + + EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex); + + Edge e = null; + if (this.validateMultiplicity(rule, traversalSource, aVertex, bVertex)) { + if (rule.getDirection().equals(Direction.OUT)) { + e = aVertex.addEdge(rule.getLabel(), bVertex); + } else if (rule.getDirection().equals(Direction.IN)) { + e = bVertex.addEdge(rule.getLabel(), aVertex); + } + + this.addProperties(e, rule); + } + return e; + } + + /** + * Adds the properties. + * + * @param edge the edge + * @param rule the rule + */ + public void addProperties(Edge edge, EdgeRule rule) { + + // In DbEdgeRules.EdgeRules -- What we have as "edgeRule" is a comma-delimited set of strings. + // The first item is the edgeLabel. + // The second in the list is always "direction" which is always OUT for the way we've implemented it. + // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to + // tags as defined in EdgeInfoMap. + // Note - if they are tagged as 'reverse', that means they get the tag name with "-REV" on it + Map<String, String> propMap = rule.getEdgeProperties(); + + for (String key : propMap.keySet()) { + String revKeyname = key + "-REV"; + String triple = propMap.get(key); + if(triple.equals("true")){ + edge.property(key, true); + edge.property(revKeyname,false); + } else if (triple.equals("false")) { + edge.property(key, false); + edge.property(revKeyname,false); + } else if (triple.equals("reverse")) { + edge.property(key, false); + edge.property(revKeyname,true); + } + } + } + + /** + * Checks for edge rule. + * + * @param outType the out type + * @param inType the in type + * @return true, if successful + */ + public boolean hasEdgeRule(String outType, String inType) { + + Collection<String> collection = rules.get(outType + "|" + inType); + + return !collection.isEmpty(); + + } + + /** + * Checks for edge rule. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return true, if successful + */ + public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex) { + String outType = (String)aVertex.<String>property("aai-node-type").orElse(null); + String inType = (String)bVertex.<String>property("aai-node-type").orElse(null); + + return this.hasEdgeRule(outType, inType); + + } + + public Map<String, EdgeRule> getEdgeRules(String outType, String inType) throws AAIException { + Map<String, EdgeRule> result = new HashMap<>(); + EdgeRule rule = null; + for (EdgeType type : EdgeType.values()) { + try { + rule = this.getEdgeRule(type, outType, inType); + result.put(rule.getLabel(), rule); + } catch (NoEdgeRuleFoundException e) { + continue; + } + } + + return result; + } + /** + * Gets the edge rule. + * + * @param outType the out type + * @param inType the in type + * @return the edge rule + * @throws AAIException the AAI exception + */ + public EdgeRule getEdgeRule(EdgeType type, String outType, String inType) throws AAIException { + EdgeRule rule = new EdgeRule(); + Collection<String> collection = null; + boolean isFlipped = false; + if (this.hasEdgeRule(outType, inType) || this.hasEdgeRule(inType, outType)) { + } else { + String detail = "No EdgeRule found for passed nodeTypes: " + outType + ", " + inType + "."; + throw new AAIException("AAI_6120", detail); + } + String key = outType + "|" + inType; + collection = rules.get(key); + + String[] info = null; + Iterator<String> iterator = collection.iterator(); + info = this.findRuleForContext(type, key, iterator); + if (info == null) { //didn't find anything in that order, look again + key = inType + "|" + outType; + collection = rules.get(key); + iterator = collection.iterator(); + info = this.findRuleForContext(type, key, iterator); + isFlipped = true; + } + if (info == null) { + throw new NoEdgeRuleFoundException("No EdgeRule found for EdgeType: " + type + " and node types: " + outType + " " + inType); + } + rule.setLabel(info[this.EDGE_NAME]); + rule.setMultiplicityRule(MultiplicityRule.valueOf(info[this.MULTIPLICITY_RULE].toUpperCase())); + rule.setHasDelTarget(info[this.HAS_DEL_TARGET]); + rule.setUsesResource(info[this.USES_RESOURCE]); + rule.setIsParent(info[this.IS_PARENT]); + rule.setServiceInfrastructure(info[this.SVC_INFRA]); + Direction direction = Direction.valueOf(info[this.DIRECTION]); + if (isFlipped && direction.equals(Direction.OUT)) { + rule.setDirection(Direction.IN); + } else if (isFlipped && direction.equals(Direction.IN)){ + rule.setDirection(Direction.OUT); + } else { + rule.setDirection(direction); + } + + return rule; + } + + private String[] findRuleForContext (EdgeType type, String key, Iterator<String> itr) { + String[] result = null; + String s = ""; + String isParent = ""; + String[] info = new String[10]; + while (itr.hasNext()) { + s = itr.next(); + info = s.split(","); + isParent = info[this.IS_PARENT]; + //lazily stop iterating if we find a match + //should there be a mismatch between type and isParent, + //the caller will receive something. + //this operates on the assumption that there are at most two rules + //for a given vertex pair + if (type.equals(EdgeType.TREE) && (isParent.equals("true") || isParent.equals("reverse"))) { + result = info; + break; + } else if (type.equals(EdgeType.COUSIN) && isParent.equals("false")) { + result = info; + break; + } + } + + + return result; + } + /** + * Gets the edge rule. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge rule + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException { + String outType = (String)aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + String inType = (String)bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + + return this.getEdgeRule(type, outType, inType); + + + } + + /** + * Gets the delete semantic. + * + * @param nodeType the node type + * @return the delete semantic + */ + public DeleteSemantic getDeleteSemantic(String nodeType) { + Collection<String> semanticCollection = deleteScope.get(nodeType); + String semantic = semanticCollection.iterator().next(); + + return DeleteSemantic.valueOf(semantic); + + } + + /** + * Validate multiplicity. + * + * @param rule the rule + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean validateMultiplicity(EdgeRule rule, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + + if (rule.getDirection().equals(Direction.OUT)) { + + } else if (rule.getDirection().equals(Direction.IN)) { + Vertex tempV = bVertex; + bVertex = aVertex; + aVertex = tempV; + } + + String aVertexType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + String bVertexType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + String label = rule.getLabel(); + MultiplicityRule multiplicityRule = rule.getMultiplicityRule(); + List<Edge> outEdges = traversalSource.V(aVertex).outE(label).where(__.inV().has(AAIProperties.NODE_TYPE, bVertexType)).toList(); + List<Edge> inEdges = traversalSource.V(bVertex).inE(label).where(__.outV().has(AAIProperties.NODE_TYPE, aVertexType)).toList(); + String detail = ""; + if (multiplicityRule.equals(MultiplicityRule.ONE2ONE)) { + if (inEdges.size() >= 1 || outEdges.size() >= 1 ) { + detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + } + } else if (multiplicityRule.equals(MultiplicityRule.ONE2MANY)) { + if (inEdges.size() >= 1) { + detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + } + } else if (multiplicityRule.equals(MultiplicityRule.MANY2ONE)) { + if (outEdges.size() >= 1) { + detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + } + } else { + + } + + if (!detail.equals("")) { + throw new AAIException("AAI_6140", detail); + } + + return true; + + } + + public Multimap<String, EdgeRule> getAllRules() throws AAIException { + + Multimap<String, EdgeRule> result = ArrayListMultimap.create(); + + for (String key : this.rules.keySet()) { + String outType = ""; + String inType = ""; + String[] split = key.split("\\|"); + outType = split[0]; + inType = split[1]; + result.putAll(key,this.getEdgeRules(outType, inType).values()); + } + + return result; + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeType.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeType.java new file mode 100644 index 00000000..aabd74fe --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/EdgeType.java @@ -0,0 +1,26 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +public enum EdgeType { + COUSIN, + TREE; +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/GetAllPool.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/GetAllPool.java new file mode 100644 index 00000000..356f70ae --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/GetAllPool.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class GetAllPool { + + private ExecutorService pool; + + private GetAllPool() { + pool = Executors.newWorkStealingPool(Runtime.getRuntime().availableProcessors()); + } + + private static class Helper { + private static final GetAllPool INSTANCE = new GetAllPool(); + } + + public static GetAllPool getInstance() { + return Helper.INSTANCE; + } + + public ExecutorService getPool() { + + return this.pool; + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/GraphSingleton.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/GraphSingleton.java new file mode 100644 index 00000000..9f226dc1 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/GraphSingleton.java @@ -0,0 +1,69 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.openecomp.aai.dbmap.AAIGraph; +import org.openecomp.aai.dbmap.DBConnectionType; +import com.thinkaurelius.titan.core.TitanGraph; + +/* This class simply calls AAIGraph under the covers for now */ +public class GraphSingleton { + + protected AtomicInteger totalCount = new AtomicInteger(); + + private static class Helper { + private static final GraphSingleton INSTANCE = new GraphSingleton(); + } + + /** + * Gets the single instance of GraphSingleton. + * + * @return single instance of GraphSingleton + */ + public static GraphSingleton getInstance() { + return Helper.INSTANCE; + + } + + /** + * Gets the count. + * + * @return the count + */ + public AtomicInteger getCount() { + return totalCount; + } + + /** + * Gets the tx graph. + * + * @return the tx graph + */ + public TitanGraph getTxGraph() { + return AAIGraph.getInstance().getGraph(); + } + + public TitanGraph getTxGraph(DBConnectionType connectionType) { + return AAIGraph.getInstance().getGraph(connectionType); + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/LegacyDBSerializer.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/LegacyDBSerializer.java new file mode 100644 index 00000000..841b1b74 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/LegacyDBSerializer.java @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.ModelType; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.apache.tinkerpop.gremlin.structure.Graph; + +public class LegacyDBSerializer extends DBSerializer { + + public LegacyDBSerializer(Version version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { + super(version, engine, introspectionType, sourceOfTruth); + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/MultiplicityRule.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/MultiplicityRule.java new file mode 100644 index 00000000..734a44bc --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/MultiplicityRule.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +public enum MultiplicityRule { + MANY2ONE, + ONE2MANY, + ONE2ONE, + MANY2MANY +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/TitanGraphSingleton.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/TitanGraphSingleton.java new file mode 100644 index 00000000..bca2d70c --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/TitanGraphSingleton.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db; + +/* This is class is just a wrapper of its parent */ +public class TitanGraphSingleton extends GraphSingleton { + + private static class Helper { + private static final TitanGraphSingleton INSTANCE = new TitanGraphSingleton(); + } + + /** + * Gets the single instance of TitanGraphSingleton. + * + * @return single instance of TitanGraphSingleton + */ + public static TitanGraphSingleton getInstance() { + return Helper.INSTANCE; + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java b/aai-core/src/main/java/org/openecomp/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java new file mode 100644 index 00000000..6647f4d5 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.db.exceptions; + +import org.openecomp.aai.exceptions.AAIException; + +public class NoEdgeRuleFoundException extends AAIException { + + private static final long serialVersionUID = -906843868234976763L; + + public NoEdgeRuleFoundException(String message) { + super("AAI_6129", message); + } + + public NoEdgeRuleFoundException(Throwable cause) { + super("AAI_6129",cause); + } + + public NoEdgeRuleFoundException(String message, Throwable cause) { + super("AAI_6129", cause, message); + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/QueryStyle.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/QueryStyle.java new file mode 100644 index 00000000..acec8dff --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/QueryStyle.java @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.engines; + +public enum QueryStyle { + GREMLIN_TRAVERSAL, GREMLIN_UNIQUE, GREMLINPIPELINE_TRAVERSAL, TRAVERSAL +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TitanDBEngine.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TitanDBEngine.java new file mode 100644 index 00000000..e3e0e857 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TitanDBEngine.java @@ -0,0 +1,103 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.engines; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.serialization.db.TitanGraphSingleton; +import com.thinkaurelius.titan.core.TitanVertex; + +public class TitanDBEngine extends TransactionalGraphEngine { + + /** + * Instantiates a new titan DB engine. + * + * @param style the style + * @param loader the loader + */ + public TitanDBEngine(QueryStyle style, DBConnectionType connectionType, Loader loader) { + super(style, loader, connectionType, TitanGraphSingleton.getInstance()); + } + + /** + * Instantiates a new titan DB engine. + * + * @param style the style + * @param loader the loader + * @param connect the connect + */ + public TitanDBEngine(QueryStyle style, Loader loader, boolean connect) { + super(style, loader); + if (connect) { + this.singleton = TitanGraphSingleton.getInstance(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean setListProperty(Vertex v, String name, List<?> objs) { + + //clear out list full replace style + + Iterator<VertexProperty<Object>> iterator = v.properties(name); + while (iterator.hasNext()) { + iterator.next().remove(); + } + if (objs != null) { + for (Object obj : objs) { + v.property(name, obj); + } + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public List<Object> getListProperty(Vertex v, String name) { + + List<Object> result = new ArrayList<Object>(); + + Iterator<VertexProperty<Object>> iterator = v.properties(name); + + while (iterator.hasNext()) { + result.add(iterator.next().value()); + } + + if (result.size() == 0) { + result = null; + } + + return result; + + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TransactionalGraphEngine.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TransactionalGraphEngine.java new file mode 100644 index 00000000..7f63c055 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/TransactionalGraphEngine.java @@ -0,0 +1,234 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.engines; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.query.builder.GremlinTraversal; +import org.openecomp.aai.query.builder.GremlinUnique; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.query.builder.TraversalQuery; +import org.openecomp.aai.serialization.db.GraphSingleton; +import org.openecomp.aai.serialization.engines.query.GraphTraversalQueryEngine; +import org.openecomp.aai.serialization.engines.query.QueryEngine; +import com.thinkaurelius.titan.core.TitanGraph; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.schema.TitanManagement; + +public abstract class TransactionalGraphEngine { + + protected GraphSingleton singleton = null; + protected QueryEngine queryEngine = null; + protected QueryBuilder queryBuilder = null; + protected QueryStyle style = null; + protected final DBConnectionType connectionType; + protected final Loader loader; + protected TitanTransaction currentTx = null; + protected GraphTraversalSource currentTraversal = null; + protected GraphTraversalSource readOnlyTraversal = null; + private final Admin admin; + /** + * Instantiates a new transactional graph engine. + * + * @param style the style + * @param loader the loader + */ + public TransactionalGraphEngine (QueryStyle style, Loader loader, DBConnectionType connectionType, GraphSingleton singleton) { + this.loader = loader; + this.style = style; + this.singleton = singleton; + this.connectionType = connectionType; + admin = new Admin(); + + } + + public TransactionalGraphEngine (QueryStyle style, Loader loader) { + this.loader = loader; + this.style = style; + this.connectionType = DBConnectionType.REALTIME; + admin = new Admin(); + } + + /** + * Sets the list property. + * + * @param v the v + * @param name the name + * @param obj the obj + * @return true, if successful + */ + public abstract boolean setListProperty(Vertex v, String name, List<?> obj); + + /** + * Gets the list property. + * + * @param v the v + * @param name the name + * @return the list property + */ + public abstract List<Object> getListProperty(Vertex v, String name); + + /** + * Gets the graph. + * + * @return the graph + */ + private TitanGraph getGraph() { + return singleton.getTxGraph(this.connectionType); + } + + /** + * Gets the count. + * + * @return the count + */ + public AtomicInteger getCount() { + return singleton.getCount(); + } + + + /** + * Gets the query engine. + * + * @return the query engine + */ + public QueryEngine getQueryEngine() { + QueryEngine engine = null; + if (style.equals(QueryStyle.GREMLIN_TRAVERSAL)) { + //this.queryEngine = new GremlinQueryEngine(this); + } else if (style.equals(QueryStyle.GREMLIN_UNIQUE)) { + //this.queryEngine = new GremlinQueryEngine(this); + } else if (style.equals(QueryStyle.GREMLINPIPELINE_TRAVERSAL)) { + //this.queryEngine = new GremlinPipelineQueryEngine(this); + } else if (style.equals(QueryStyle.TRAVERSAL)) { + + return new GraphTraversalQueryEngine(this.asAdmin().getTraversalSource()); + + } else { + throw new IllegalArgumentException("Query Engine type not recognized"); + } + + return engine; + } + + /** + * Gets the query builder. + * + * @return the query builder + */ + public QueryBuilder getQueryBuilder() { + return getQueryBuilder(this.loader); + } + + public QueryBuilder getQueryBuilder(Loader loader) { + if (style.equals(QueryStyle.GREMLIN_TRAVERSAL)) { + return new GremlinTraversal(loader, this.asAdmin().getTraversalSource()); + } else if (style.equals(QueryStyle.GREMLIN_UNIQUE)) { + return new GremlinUnique(loader, this.asAdmin().getTraversalSource()); + } else if (style.equals(QueryStyle.GREMLINPIPELINE_TRAVERSAL)) { + //return new GremlinPipelineTraversal(loader); + } else if (style.equals(QueryStyle.TRAVERSAL)) { + return new TraversalQuery(loader, this.asAdmin().getTraversalSource()); + } else { + throw new IllegalArgumentException("Query Builder type not recognized"); + } + return queryBuilder; + } + /** + * Gets the query builder. + * + * @param start the start + * @return the query builder + */ + public QueryBuilder getQueryBuilder(Vertex start) { + return getQueryBuilder(this.loader, start); + } + + public QueryBuilder getQueryBuilder(Loader loader, Vertex start) { + if (style.equals(QueryStyle.GREMLIN_TRAVERSAL)) { + return new GremlinTraversal(loader, this.asAdmin().getTraversalSource(), start); + } else if (style.equals(QueryStyle.GREMLIN_UNIQUE)) { + return new GremlinUnique(loader, this.asAdmin().getTraversalSource(), start); + } else if (style.equals(QueryStyle.GREMLINPIPELINE_TRAVERSAL)) { + //return new GremlinPipelineTraversal(loader,start); + } else if (style.equals(QueryStyle.TRAVERSAL)) { + return new TraversalQuery(loader, this.asAdmin().getTraversalSource(), start); + } else { + throw new IllegalArgumentException("Query Builder type not recognized"); + } + return queryBuilder; + } + + public TitanTransaction startTransaction() { + if (this.tx() == null) { + this.currentTx = this.getGraph().newTransaction(); + this.currentTraversal = this.tx().traversal(); + this.readOnlyTraversal = this.tx().traversal(GraphTraversalSource.build().with(ReadOnlyStrategy.instance())); + } + return currentTx; + } + + public void rollback() { + if (this.tx() != null) { + this.tx().rollback(); + this.currentTx = null; + this.currentTraversal = null; + this.readOnlyTraversal = null; + } + } + public void commit() { + if (this.tx() != null) { + this.tx().commit(); + this.currentTx = null; + this.currentTraversal = null; + this.readOnlyTraversal = null; + } + } + + public TitanTransaction tx() { + return this.currentTx; + } + + public Admin asAdmin() { + return admin; + } + + public class Admin { + + public GraphTraversalSource getTraversalSource() { + return currentTraversal; + } + public GraphTraversalSource getReadOnlyTraversalSource() { + return readOnlyTraversal; + } + + public TitanManagement getManagementSystem() { + return getGraph().openManagement(); + } + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GraphTraversalQueryEngine.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GraphTraversalQueryEngine.java new file mode 100644 index 00000000..2f485b10 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GraphTraversalQueryEngine.java @@ -0,0 +1,170 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.engines.query; + +import java.util.List; +import java.util.Set; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.introspection.Loader; + +/* + * This class needs some big explanation despite its compact size. + * This controls all the queries performed by the CRUD API in A&AI. + * findParents, findChildren, and findDeletable require special attention + * These methods use 'repeat'. You cannot use 'emit' with repeat currently + * as it is extremely buggy as of tinkerpop-3.0.1-incubating. The way around + * it (for now) is to sideEffect all the vertices we traverse into an ArrayList. + * + */ +public class GraphTraversalQueryEngine extends QueryEngine { + + /** + * Instantiates a new graph traversal query engine. + * + * @param graphEngine the graph engine + */ + public GraphTraversalQueryEngine(GraphTraversalSource g) { + super(g); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findParents(Vertex start) { + + final GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true).repeat(__.inE().has("isParent", true).outV()); + return pipe.toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findAllChildren(Vertex start) { + + GraphTraversal<Vertex, Vertex> pipe = this.g + .V(start).emit(v -> true).repeat(__.outE().has("isParent", true).inV()); + + + return pipe.toList(); + + } + + public List<Vertex> findChildrenOfType(Vertex start, String type) { + GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).union( + __.outE().has("isParent", true).inV(), + __.inE().has("isParent-REV", true).outV() + ).has(AAIProperties.NODE_TYPE, type).dedup(); + + return pipe.toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findDeletable(Vertex start) { + GraphTraversal<Vertex, Vertex> pipe = this.g + .V(start).emit(v -> true).repeat(__.outE().or( + __.has("isParent", true), + __.has("hasDelTarget", true)).inV()); + + return pipe.toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findRelatedVertices(Vertex start, Direction direction, String label, String nodeType) { + GraphTraversal<Vertex, Vertex> pipe = this.g.V(start); + switch (direction) { + case OUT: + pipe.out(label); + break; + case IN: + pipe.in(label); + break; + case BOTH: + pipe.both(label); + break; + default: + break; + } + + pipe.has(AAIProperties.NODE_TYPE, nodeType).dedup(); + return pipe.toList(); + } + + @Override + public Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly) { + final GraphTraversal<Vertex, ?> t = this.g.V(start).emit(v -> true).times(iterations).repeat( + __.outE().has("isParent", true).inV()); + + if (!nodeOnly) { + t.union( + __.identity(), + __.bothE().has("isParent", false).dedup().otherV() + ); + } + t.tree(); + if (t.hasNext()) { + return (Tree)t.next(); + } else { + return new Tree(); + } + } + + @Override + public List<Edge> findEdgesForVersion(Vertex start, Loader loader) { + final Set<String> objects = loader.getAllObjects().keySet(); + GraphTraversal<Vertex, Edge> pipeline = this.g.V(start).union( + __.inE().has("isParent", false).has("isParent-REV", false).where(__.outV().has(AAIProperties.NODE_TYPE, P.within(objects))), + __.outE().has("isParent", false).has("isParent-REV", false).where(__.inV().has(AAIProperties.NODE_TYPE, P.within(objects))) + ).dedup(); + + return pipeline.toList(); + } + + + @Override + public List<Vertex> findCousinVertices(Vertex start) { + GraphTraversal<Vertex, Vertex> pipeline = this.g.V(start).union( + __.inE().has("isParent", false).has("isParent-REV", false), + __.outE().has("isParent", false).has("isParent-REV", false)).otherV().dedup(); + + return pipeline.toList(); + } + +} + diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GremlinPipelineQueryEngine.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GremlinPipelineQueryEngine.java new file mode 100644 index 00000000..adf5d405 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/GremlinPipelineQueryEngine.java @@ -0,0 +1,185 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +/* +package org.openecomp.aai.serialization.engines.query; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import com.tinkerpop.blueprints.Direction; +import com.tinkerpop.blueprints.TransactionalGraph; +import com.tinkerpop.blueprints.Vertex; +import com.tinkerpop.gremlin.java.GremlinPipeline; +import com.tinkerpop.pipes.IdentityPipe; +import com.tinkerpop.pipes.PipeFunction; +import com.tinkerpop.pipes.branch.LoopPipe; + +public class GremlinPipelineQueryEngine extends QueryEngine { + + public GremlinPipelineQueryEngine(TransactionalGraphEngine graphEngine) { + super(graphEngine); + } + + @Override + public List<Vertex> executeQuery(TransactionalGraph g, QueryBuilder query) { + List<Vertex> results = null; + Vertex start = query.getStart(); + if (start != null) { + results = ((GremlinPipeline)query.getQuery()).cast(Vertex.class).toList(); + } else { + GremlinPipeline pipe = new GremlinPipeline(g); + results = process(pipe, (GremlinPipeline)query.getQuery()); + + } + return results; + } + + @Override + public List<Vertex> executeParentQuery(TransactionalGraph g, QueryBuilder query) { + List<Vertex> results = null; + Vertex start = query.getStart(); + if (start != null) { + results = ((GremlinPipeline)query.getParentQuery()).cast(Vertex.class).toList(); + } else { + GremlinPipeline pipe = new GremlinPipeline(g); + results = process(pipe, (GremlinPipeline)query.getParentQuery()); + + } + return results; + } + + @Override + public List<Vertex> findParents(Vertex start) { + GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline(start).as("x").inE() + .has("isParent", true).outV().loop("x", new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + GremlinPipeline<Vertex, Long> pipe = new GremlinPipeline<>(argument.getObject()); + return pipe.inE().has("isParent", true).count() == 1 || argument.getLoops() < 100; + } + + }, new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + return true; + } + + }); + + List<Vertex> results = pipe.toList(); + results.add(0, start); + return results; + } + + @Override + public List<Vertex> findChildren(Vertex start) { + Set<Vertex> seen = new HashSet<>(); + seen.add(start); + GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline(start).as("x").outE().has("isParent", true).inV() + .except(seen).store(seen).loop("x", new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + GremlinPipeline<Vertex, Long> pipe = new GremlinPipeline<>(argument.getObject()); + return pipe.outE().has("isParent", true).count() >= 1 || argument.getLoops() < 100; + } + + }, new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + return true; + } + + }); + + List<Vertex> results = pipe.toList(); + results.add(0, start); + return results; + } + + @Override + public List<Vertex> findDeletable(Vertex start) { + Set<Vertex> seen = new HashSet<>(); + seen.add(start); + GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline<Vertex, Vertex>(start).as("x").outE().or( + new GremlinPipeline(new IdentityPipe()).has("isParent", true), + new GremlinPipeline(new IdentityPipe()).has("hasDelTarget", true)).inV() + .except(seen).store(seen).loop("x", new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + GremlinPipeline<Vertex, Long> pipe = new GremlinPipeline<>(argument.getObject()); + return pipe.outE().or( + new GremlinPipeline(new IdentityPipe()).has("isParent", true), + new GremlinPipeline(new IdentityPipe()).has("hasDelTarget", true)).count() >= 1 || argument.getLoops() < 100; + } + + }, new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() { + + @Override + public Boolean compute(LoopPipe.LoopBundle<Vertex> argument) { + return true; + } + + }); + List<Vertex> results = pipe.toList(); + results.add(0, start); + + return results; + } + + private List<Vertex> process(GremlinPipeline start, GremlinPipeline pipe) { + + + return start.add(pipe).cast(Vertex.class).toList(); + } + + @Override + public List<Vertex> findRelatedVertices(Vertex start, Direction direction, String label, String nodeType) { + GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline<Vertex, Vertex>(start); + switch (direction) { + case OUT: + pipe.out(label); + break; + case IN: + pipe.in(label); + break; + case BOTH: + pipe.both(label); + break; + default: + break; + } + + pipe.has(AAIProperties.NODE_TYPE, nodeType).dedup(); + List<Vertex> result = pipe.toList(); + return result; + } + +} +*/ diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/QueryEngine.java b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/QueryEngine.java new file mode 100644 index 00000000..bff6f082 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/engines/query/QueryEngine.java @@ -0,0 +1,92 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.engines.query; + +import java.util.List; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.introspection.Loader; + +public abstract class QueryEngine { + + final protected GraphTraversalSource g; + + /** + * Instantiates a new query engine. + * + * @param graphEngine the graph engine + */ + public QueryEngine (GraphTraversalSource g) { + this.g = g; + } + + /** + * Find parents. + * + * @param start the start + * @return the list + */ + public abstract List<Vertex> findParents(Vertex start); + + /** + * Find children. + * + * @param start the start + * @return the list + */ + public abstract List<Vertex> findAllChildren(Vertex start); + + public abstract List<Vertex> findChildrenOfType(Vertex start, String type); + /** + * Find deletable. + * + * @param start the start + * @return the list + */ + public abstract List<Vertex> findDeletable(Vertex start); + + public Tree<Element> findSubGraph(Vertex start) { + return findSubGraph(start, AAIProperties.MAXIMUM_DEPTH, false); + } + public abstract Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly); + /** + * Find related vertices. + * + * @param start the start + * @param direction the direction + * @param label the label + * @param nodeType the node type + * @return the list + */ + public abstract List<Vertex> findRelatedVertices(Vertex start, Direction direction, String label, String nodeType); + + public abstract List<Edge> findEdgesForVersion(Vertex start, Loader loader); + + public abstract List<Vertex> findCousinVertices(Vertex start); + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Console.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Console.java new file mode 100644 index 00000000..7aaf02c8 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Console.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import com.google.gson.JsonObject; + +public class Console implements FormatMapper { + + @Override + public JsonObject formatObject(Object v) throws AAIFormatVertexException { + + JsonObject json = new JsonObject(); + json.addProperty("result", v.toString()); + + return json; + } + + @Override + public int parallelThreshold() { + return 100; + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Format.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Format.java new file mode 100644 index 00000000..e3618917 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Format.java @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +public enum Format { + graphson, + pathed, id, resource, + simple, + resource_and_url, + console +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatFactory.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatFactory.java new file mode 100644 index 00000000..d00b5cda --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatFactory.java @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; + +public class FormatFactory { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + public FormatFactory (Loader loader, DBSerializer serializer) throws AAIException { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = new UrlBuilder(loader.getVersion(), serializer); + } + + public Formatter get(Format format) throws AAIException { + + Formatter formattter = null; + + switch (format) { + case graphson : + formattter = new Formatter(new GraphSON()); + break; + case pathed : + formattter = new Formatter(new PathedURL(loader, urlBuilder)); + break; + case id : + formattter = new Formatter(new IdURL(loader, urlBuilder)); + break; + case resource : + formattter = new Formatter(new Resource.Builder(loader, serializer, urlBuilder).build()); + break; + case resource_and_url : + formattter = new Formatter(new Resource.Builder(loader, serializer, urlBuilder).includeUrl().build()); + break; + case simple : + formattter = new Formatter(new SimpleFormat(urlBuilder)); + break; + case console : + formattter = new Formatter(new Console()); + break; + default : + break; + } + + return formattter; + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatMapper.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatMapper.java new file mode 100644 index 00000000..def68e0f --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/FormatMapper.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import com.google.gson.JsonObject; + +public interface FormatMapper { + + public JsonObject formatObject(Object v) throws AAIFormatVertexException; + + public int parallelThreshold(); +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Formatter.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Formatter.java new file mode 100644 index 00000000..f2bafe24 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Formatter.java @@ -0,0 +1,75 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class Formatter { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Formatter.class); + + protected JsonParser parser = new JsonParser(); + protected final FormatMapper format; + public Formatter(FormatMapper format) { + this.format = format; + } + + public JsonObject output(List<Object> vertices) { + Stream<Object> stream = null; + JsonObject result = new JsonObject(); + JsonArray body = new JsonArray(); + if (vertices.size() >= format.parallelThreshold()) { + stream = vertices.parallelStream(); + } else { + stream = vertices.stream(); + } + + stream.map(v -> { + try { + return Optional.<JsonObject>of(format.formatObject(v)); + } catch (AAIFormatVertexException e) { + LOGGER.warn("Failed to format vertex, returning a partial list", e); + } + + return Optional.<JsonObject>empty(); + }).forEach(obj -> { + if (obj.isPresent()) { + synchronized (body) { + body.add(obj.get()); + } + } + }); + + result.add("results", body); + + return result.getAsJsonObject(); + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/GraphSON.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/GraphSON.java new file mode 100644 index 00000000..69bd1b24 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/GraphSON.java @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.thinkaurelius.titan.graphdb.tinkerpop.TitanIoRegistry; + +public class GraphSON implements FormatMapper { + + private final GraphSONMapper mapper = GraphSONMapper.build().addRegistry(TitanIoRegistry.INSTANCE).create(); + private final GraphSONWriter writer = GraphSONWriter.build().mapper(mapper).create(); + protected JsonParser parser = new JsonParser(); + + @Override + public JsonObject formatObject(Object v) { + OutputStream os = new ByteArrayOutputStream(); + String result = ""; + try { + writer.writeVertex(os, (Vertex)v, Direction.BOTH); + + result = os.toString(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return parser.parse(result).getAsJsonObject(); + + } + + @Override + public int parallelThreshold() { + return 50; + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/IdURL.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/IdURL.java new file mode 100644 index 00000000..a7d641dd --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/IdURL.java @@ -0,0 +1,71 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class IdURL implements FormatMapper { + + private final UrlBuilder urlBuilder; + private final JsonParser parser; + private final Loader loader; + + public IdURL (Loader loader, UrlBuilder urlBuilder) throws AAIException { + this.urlBuilder = urlBuilder; + this.parser = new JsonParser(); + this.loader = loader; + } + + @Override + public int parallelThreshold() { + return 2500; + } + + @Override + public JsonObject formatObject(Object input) throws AAIFormatVertexException { + Vertex v = (Vertex)input; + try { + final Introspector searchResult = this.loader.introspectorFromName("result-data"); + + searchResult.setValue("resource-type", v.value(AAIProperties.NODE_TYPE)); + searchResult.setValue("resource-link", this.urlBuilder.id(v)); + + final String json = searchResult.marshal(false); + + return parser.parse(json).getAsJsonObject(); + + } catch (AAIUnknownObjectException e) { + throw new RuntimeException("Fatal error - result-data object does not exist!"); + } + + + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/PathedURL.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/PathedURL.java new file mode 100644 index 00000000..dda9f4af --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/PathedURL.java @@ -0,0 +1,70 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public final class PathedURL implements FormatMapper { + + private final UrlBuilder urlBuilder; + private final JsonParser parser; + private final Loader loader; + + public PathedURL (Loader loader, UrlBuilder urlBuilder) throws AAIException { + this.urlBuilder = urlBuilder; + this.parser = new JsonParser(); + this.loader = loader; + } + + @Override + public int parallelThreshold() { + return 20; + } + + @Override + public JsonObject formatObject(Object input) throws AAIFormatVertexException { + Vertex v = (Vertex)input; + try { + final Introspector searchResult = this.loader.introspectorFromName("result-data"); + + searchResult.setValue("resource-type", v.value(AAIProperties.NODE_TYPE)); + + searchResult.setValue("resource-link", this.urlBuilder.pathed(v)); + final String json = searchResult.marshal(false); + return this.parser.parse(json).getAsJsonObject(); + + } catch (AAIUnknownObjectException e) { + throw new RuntimeException("Fatal error - result-data does not exist!", e); + } + + } + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Resource.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Resource.java new file mode 100644 index 00000000..7859719e --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/Resource.java @@ -0,0 +1,142 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class Resource implements FormatMapper { + + private final Loader loader; + private final DBSerializer serializer; + private final JsonParser parser; + private final UrlBuilder urlBuilder; + private final boolean includeUrl; + + private Resource (Builder builder) { + this.parser = new JsonParser(); + this.loader = builder.getLoader(); + this.serializer = builder.getSerializer(); + this.urlBuilder = builder.getUrlBuilder(); + this.includeUrl = builder.isIncludeUrl(); + } + + @Override + public JsonObject formatObject(Object input) throws AAIFormatVertexException { + Vertex v = (Vertex)input; + JsonObject json = new JsonObject(); + + if (this.includeUrl) { + json.addProperty("url", this.urlBuilder.pathed(v)); + } + json.add(v.<String>property(AAIProperties.NODE_TYPE) + .orElse(null), this.vertexToJsonObject(v)); + + return json; + } + + protected JsonObject vertexToJsonObject(Vertex v) throws AAIFormatVertexException { + try { + final Introspector obj = getLoader().introspectorFromName( + v.<String>property(AAIProperties.NODE_TYPE) + .orElse(null) + ); + + final List<Vertex> wrapper = new ArrayList<>(); + + wrapper.add(v); + + try { + getSerializer().dbToObject(wrapper, obj, 1, false, "false"); + } catch (AAIException | UnsupportedEncodingException e) { + throw new AAIFormatVertexException("Failed to format vertex - error while serializing: " + e.getMessage(), e); + } + + final String json = obj.marshal(false); + + return getParser().parse(json).getAsJsonObject(); + } catch (AAIUnknownObjectException e) { + throw new AAIFormatVertexException("Failed to format vertex - unknown object", e); + } + } + + @Override + public int parallelThreshold() { + return 20; + } + + private Loader getLoader() { return loader; } + private DBSerializer getSerializer() { return serializer; } + private JsonParser getParser() { return parser; } + + public static class Builder { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + private boolean includeUrl = false; + + public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = urlBuilder; + } + + protected Loader getLoader() { + return this.loader; + } + + protected DBSerializer getSerializer() { + return this.serializer; + } + + protected UrlBuilder getUrlBuilder() { + return this.urlBuilder; + } + + public Builder includeUrl() { + this.includeUrl = true; + return this; + } + + protected boolean isIncludeUrl() { + return this.includeUrl; + } + + public Resource build() { + return new Resource(this); + } + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SimpleFormat.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SimpleFormat.java new file mode 100644 index 00000000..222b0922 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SimpleFormat.java @@ -0,0 +1,114 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class SimpleFormat implements FormatMapper { + + private final UrlBuilder urlBuilder; + private final Set<String> blacklist; + private final Collection<String> props = Arrays.asList(AAIProperties.AAI_URI, AAIProperties.NODE_TYPE); + public SimpleFormat(UrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + this.blacklist = new HashSet<>(); + blacklist.addAll(props); + } + + @Override + public JsonObject formatObject(Object input) throws AAIFormatVertexException { + Vertex v = (Vertex)input; + JsonObject json = new JsonObject(); + json.addProperty("id", v.id().toString()); + json.addProperty("node-type", v.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(v)); + json.add("properties", this.createPropertiesObject(v)); + json.add("related-to", this.createRelationshipObject(v)); + + return json; + } + + @Override + public int parallelThreshold() { + return 100; + } + + private JsonObject createPropertiesObject(Vertex v) { + JsonObject json = new JsonObject(); + Iterator<VertexProperty<Object>> iter = v.properties(); + + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + if (!blacklist.contains(prop.key())) { + if (prop.value() instanceof String) { + json.addProperty(prop.key(), (String)prop.value()); + } else if (prop.value() instanceof Boolean) { + json.addProperty(prop.key(), (Boolean)prop.value()); + } else if (prop.value() instanceof Number) { + json.addProperty(prop.key(), (Number)prop.value()); + } else if (prop.value() instanceof List) { + Gson gson = new Gson(); + String list = gson.toJson(prop.value()); + + json.addProperty(prop.key(), list); + } else { + //throw exception? + return null; + } + } + } + + return json; + } + + private JsonArray createRelationshipObject(Vertex v) throws AAIFormatVertexException { + JsonArray jarray = new JsonArray(); + Iterator<Vertex> iter = v.vertices(Direction.BOTH); + + while (iter.hasNext()) { + Vertex related = iter.next(); + + JsonObject json = new JsonObject(); + json.addProperty("id", related.id().toString()); + json.addProperty("node-type", related.<String>value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(related)); + jarray.add(json); + } + + return jarray; + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SubGraphStyle.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SubGraphStyle.java new file mode 100644 index 00000000..7fa30431 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/SubGraphStyle.java @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats; + +public enum SubGraphStyle { + star, + prune, + no_op +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java new file mode 100644 index 00000000..692f4f65 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats.exceptions; + +public class AAIFormatVertexException extends Exception { + + private static final long serialVersionUID = -5814240841844624097L; + + public AAIFormatVertexException() {} + + public AAIFormatVertexException(String message) { + super(message); + } + + public AAIFormatVertexException(Throwable cause) { + super(cause); + } + + public AAIFormatVertexException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/utils/UrlBuilder.java b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/utils/UrlBuilder.java new file mode 100644 index 00000000..1c79d24b --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/queryformats/utils/UrlBuilder.java @@ -0,0 +1,74 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.queryformats.utils; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.openecomp.aai.util.AAIApiServerURLBase; +import org.openecomp.aai.workarounds.LegacyURITransformer; + +public class UrlBuilder { + + private final DBSerializer serializer; + private final Version version; + private final String serverBase; + + public UrlBuilder (Version version, DBSerializer serializer) throws AAIException { + this.serializer = serializer; + this.version = version; + this.serverBase = AAIApiServerURLBase.get(AAIProperties.LATEST); + } + + public String pathed(Vertex v) throws AAIFormatVertexException { + + try { + final StringBuilder result = new StringBuilder(); + final URI uri = this.serializer.getURIForVertex(v); + + result.append(this.serverBase); + result.append(this.version); + result.append(uri.getRawPath()); + + return result.toString(); + } catch (UnsupportedEncodingException | IllegalArgumentException | SecurityException e) { + throw new AAIFormatVertexException(e); + } + } + + public String id(Vertex v) { + final StringBuilder result = new StringBuilder(); + + result.append("/resources/id/" + v.id()); + result.insert(0, this.version); + result.insert(0, this.serverBase); + + return result.toString(); + } +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedEdge.java b/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedEdge.java new file mode 100644 index 00000000..36fb6a87 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedEdge.java @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.tinkerpop; + +import java.util.Iterator; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; + + +/** + * Represents a {@link Edge} that is disconnected from a {@link Graph} however, + * traversals are supported as they are backed by a Tree with saturated {@link Vertex} and {@link Edge} objects. + * These objects are not mutable and can only be used to read information out. + * + */ +public class TreeBackedEdge extends DetachedEdge implements Edge { + + private static final long serialVersionUID = 5419650145562077538L; + private TreeBackedVertex inVertex; + private TreeBackedVertex outVertex; + public TreeBackedEdge(Edge edge, TreeBackedVertex inVertex, TreeBackedVertex outVertex) { + super(edge, true); + this.inVertex = inVertex; + this.outVertex = outVertex; + } + + @Override + public Vertex inVertex() { + return this.inVertex; + } + + @Override + public Vertex outVertex() { + return this.outVertex; + } + + @Override + public Iterator<Vertex> bothVertices() { + return this.vertices(Direction.BOTH); + } + + @Override + public Iterator<Vertex> vertices(Direction direction) { + switch (direction) { + case OUT: + return IteratorUtils.of(this.outVertex); + case IN: + return IteratorUtils.of(this.inVertex); + default: + return IteratorUtils.of(this.outVertex, this.inVertex); + } + } + + + + +} diff --git a/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedVertex.java b/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedVertex.java new file mode 100644 index 00000000..92655803 --- /dev/null +++ b/aai-core/src/main/java/org/openecomp/aai/serialization/tinkerpop/TreeBackedVertex.java @@ -0,0 +1,166 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.serialization.tinkerpop; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; + +/** + * Represents a {@link Vertex} that is disconnected from a {@link Graph} however, + * traversals are supported as they are backed by a Tree with saturated {@link Vertex} and {@link Edge} objects. + * These objects are not mutable and can only be used to read information out. + * + */ + +public class TreeBackedVertex extends DetachedVertex implements Vertex { + + private static final long serialVersionUID = -976854460992756953L; + private final Tree<Element> tree; + private final Vertex self; + public TreeBackedVertex (Vertex v, Tree<Element> tree) { + super(v, true); + this.self = v; + this.tree = tree; + } + + @Override + public Iterator<Edge> edges(final Direction direction, final String... edgeLabels) { + final List<Element> edges = tree.getObjectsAtDepth(2); + final List<Tree<Element>> trees = tree.getTreesAtDepth(2); + final List<Tree<Element>> vTrees = tree.getTreesAtDepth(3); + return edges.stream().map( ele -> (Edge)ele).filter(e -> { + if (Direction.IN.equals(direction)) { + return e.inVertex().equals(self); + } else if (Direction.OUT.equals(direction)) { + return e.outVertex().equals(self); + } else { + return true; + } + }).filter(e -> { + boolean result = false; + if (edgeLabels.length == 0) { + return true; + } + for (String label : edgeLabels) { + if (label.equals(e.label())) { + result = true; + break; + } + } + return result; + }).map(e -> { + Tree<Element> eTree = new Tree<>(); + for (Tree<Element> tree : trees) { + if (tree.keySet().contains(e)) { + eTree = tree; + break; + } + } + TreeBackedVertex in = null; + TreeBackedVertex out = null; + if (e.inVertex().equals(self)) { + in = this; + out = this.createForVertex(e.outVertex(), vTrees); + } else if (e.outVertex().equals(self)) { + out = this; + in = this.createForVertex(e.inVertex(), vTrees); + } + return (Edge)new TreeBackedEdge(e, in, out); + }).iterator(); + + } + + private TreeBackedVertex createForVertex(Vertex v, List<Tree<Element>> trees) { + Tree<Element> vTree = new Tree<>(); + for (Tree<Element> tree : trees) { + if (tree.keySet().contains(v)) { + vTree = tree; + break; + } + } + + return new TreeBackedVertex((Vertex)vTree.keySet().iterator().next(), vTree); + } + @Override + public Iterator<Vertex> vertices(final Direction direction, final String... labels) { + final List<Tree<Element>> vertexElements = tree.getTreesAtDepth(3); + final List<Element> edgeElements = tree.getObjectsAtDepth(2); + return edgeElements.stream().map( ele -> (Edge)ele).filter(e -> { + boolean result = false; + if (labels.length == 0) { + return true; + } + for (String label : labels) { + if (label.equals(e.label())) { + result = true; + break; + } + } + return result; + }).filter(e -> { + if (Direction.IN.equals(direction) && e.inVertex().equals(self)) { + return true; + } else if (Direction.OUT.equals(direction) && e.outVertex().equals(self)) { + return true; + } else if (Direction.BOTH.equals(direction)){ + return true; + } else { + return false; + } + }).map(e -> { + final List<Vertex> list; + if (Direction.IN.equals(direction)) { + list = Collections.singletonList(e.outVertex()); + } else if (Direction.OUT.equals(direction)){ + list = Collections.singletonList(e.inVertex()); + } else { + list = new ArrayList<>(); + Iterator<Vertex> itr = e.bothVertices(); + while (itr.hasNext()) { + list.add(itr.next()); + } + } + return list; + + }).flatMap(list -> list.stream()).map(v -> { + Tree<Element> vTree = new Tree<Element>(); + for (Tree<Element> tree : vertexElements) { + if (tree.keySet().contains(v)) { + vTree = tree; + break; + } + } + + return (Vertex)new TreeBackedVertex(v, vTree); + }).iterator(); + } + +} |