diff options
author | 2017-09-28 12:43:08 -0400 | |
---|---|---|
committer | 2017-09-28 13:29:27 -0400 | |
commit | 1767960099ea2983eef5218b8ce44b167ae0774c (patch) | |
tree | 4efe13601c1f9ebebc6026b32ec749778cd96caf /aai-core/src/main/java/org/onap/aai/serialization | |
parent | a8a05641920545aa094dffdb016b7b5febe9a879 (diff) |
Change openecomp to onap and update license
Issue-ID: AAI-61 AAI-82
Change-Id: Iae98d4bf4c693c0a3203158bff98c3c5e739f453
Signed-off-by: Venkata Harish K Kajur <vk250x@att.com>
Diffstat (limited to 'aai-core/src/main/java/org/onap/aai/serialization')
45 files changed, 5443 insertions, 0 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/AAIDirection.java b/aai-core/src/main/java/org/onap/aai/serialization/db/AAIDirection.java new file mode 100644 index 00000000..1dce588d --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/AAIDirection.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +public enum AAIDirection { + IN, OUT, BOTH, NONE; + + public AAIDirection opposite() { + if (this.equals(OUT)) + return IN; + else if (this.equals(IN)) + return OUT; + else + return BOTH; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java new file mode 100644 index 00000000..dd073dc7 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java @@ -0,0 +1,1444 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.*; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.introspection.sideeffect.DataCopy; +import org.onap.aai.introspection.sideeffect.DataLinkReader; +import org.onap.aai.introspection.sideeffect.DataLinkWriter; +import org.onap.aai.introspection.sideeffect.SideEffectRunner; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.parsers.uri.URIParser; +import org.onap.aai.parsers.uri.URIToRelationshipObject; +import org.onap.aai.query.builder.QueryBuilder; +import org.onap.aai.schema.enums.ObjectMetadata; +import org.onap.aai.schema.enums.PropertyMetadata; +import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.tinkerpop.TreeBackedVertex; +import org.onap.aai.util.AAIApiServerURLBase; +import org.onap.aai.util.AAIConfig; +import org.onap.aai.util.AAIConstants; +import org.onap.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 + */ + 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); + + } + + private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { + + v.property(AAIProperties.NODE_TYPE, nodeType); + touchStandardVertexProperties(v, isNewVertex); + + } + + + + /** + * 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(); + touchStandardVertexProperties(wrappedObject.getDbName(), 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; + if (!obj.isContainer()) { + this.touchStandardVertexProperties(obj.getDbName(), v, 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 (!value.equals(v.property(dbProperty).orElse(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 { + if (!edgeRules.hasEdgeRule(v, cousinVertex)) { + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " + + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + "."); + } + 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) { + String uri; + uri = obj.getURI(); + child.property(AAIProperties.AAI_URI, parentUri + uri); + } + 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.getContains().equals(AAIDirection.NONE.toString())) { + //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 != 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, UnsupportedEncodingException { + 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, UnsupportedEncodingException { + 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 + Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp)); + //damaged vertex found, ignore + if (!tuple.isPresent()) { + return null; + } + List<Introspector> list = tuple.get().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.get().getValue0().id().toString() + ": " + e.getMessage(), e); + if ("true".equals(cleanUp)) { + this.deleteWithTraversal(tuple.get().getValue0()); + } + return null; + } + + if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){ + this.addRelatedToProperty(result, list.get(0)); + } + + 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 = UriBuilder.fromPath("/unknown-uri").build(); + + String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); + + if (aaiUri != null && !overwrite) { + uri = UriBuilder.fromPath(aaiUri).build(); + } else { + Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false); + if (tuple.isPresent()) { + List<Introspector> list = tuple.get().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 Optional<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 Optional.empty(); + } + + return Optional.of(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 + */ + public 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 = true; + String nodeType = ""; + 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)) { + } + List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertex).union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)).dedup().toList(); + + if (preventDeleteVertices.size() > 0) { + aaiExceptionCode = "AAI_6110"; + errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); + result = false; + } + 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/onap/aai/serialization/db/DeleteSemantic.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DeleteSemantic.java new file mode 100644 index 00000000..a6fe22b8 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DeleteSemantic.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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/onap/aai/serialization/db/EdgeProperties.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperties.java new file mode 100644 index 00000000..73ece7e4 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperties.java @@ -0,0 +1,44 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +public class EdgeProperties { + + public static String out(EdgeProperty prop) { + + return out(prop.toString()); + } + + public static String in(EdgeProperty prop) { + return in(prop.toString()); + } + + public static String out(String prop) { + + return prop; + } + + public static String in(String prop) { + return prop + "-REV"; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java new file mode 100644 index 00000000..29d82298 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +public enum EdgeProperty { + FROM("from"), + TO("to"), + LABEL("label"), + DIRECTION("direction"), + MULTIPLICITY("multiplicity"), + CONTAINS("contains-other-v"), + DELETE_OTHER_V("delete-other-v"), + SVC_INFRA("SVC-INFRA"), + PREVENT_DELETE("prevent-delete"); + + private final String name; + + private EdgeProperty(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgePropertyMap.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgePropertyMap.java new file mode 100644 index 00000000..92643bcf --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgePropertyMap.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +import org.apache.tinkerpop.gremlin.structure.Direction; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EdgePropertyMap<K, V> extends HashMap<K, V> { + + private static final long serialVersionUID = -8298355506617458683L; + + private static final Pattern variablePattern = Pattern.compile("(!)?\\$\\{(\\w+)\\}"); + + @Override + public V get(Object arg0) { + + V value = super.get(arg0); + + Matcher m = variablePattern.matcher(value.toString()); + if (m.find()) { + if (m.groupCount() == 2) { + if (m.group(1) == null) { + value = super.get(m.group(2)); + } else { + value = reverse(super.get(m.group(2))); + } + } + } + + return value; + } + + + protected V reverse(V value) { + + return (V)Direction.valueOf(value.toString()).opposite().toString(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java new file mode 100644 index 00000000..309dbffe --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java @@ -0,0 +1,211 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +import org.apache.tinkerpop.gremlin.structure.Direction; + +import java.util.HashMap; +import java.util.Map; + +public class EdgeRule { + + private String label = ""; + private MultiplicityRule multiplicityRule = null; + private Direction direction = null; + private Map<EdgeProperty, String> edgeProperties = null; + + /** + * 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; + } + + public void setMultiplicityRule(String multiplicity){ + if ("Many2Many".equalsIgnoreCase(multiplicity)) { + this.multiplicityRule = MultiplicityRule.MANY2MANY; + } else if ("One2Many".equalsIgnoreCase(multiplicity)) { + this.multiplicityRule = MultiplicityRule.ONE2MANY; + } else if ("One2One".equalsIgnoreCase(multiplicity)) { + this.multiplicityRule = MultiplicityRule.ONE2ONE; + } else { //should be "Many2One" + this.multiplicityRule = MultiplicityRule.MANY2ONE; + } + } + + /** + * 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; + } + + public void setDirection(String direction){ + if ("OUT".equalsIgnoreCase(direction)) { + this.direction = Direction.OUT; + } else if ("IN".equalsIgnoreCase(direction)) { + this.direction = Direction.IN; + } else { + this.direction = Direction.BOTH; + } + } + + /** + * 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 getContains() { + return this.getProp(EdgeProperty.CONTAINS); + } + + /** + * Sets the checks if is parent. + * + * @param isParent the new checks if is parent + */ + public void setContains(String isParent) { + this.setProp(EdgeProperty.CONTAINS, isParent); + } + + /** + * Gets the checks for del target. + * + * @return the checks for del target + */ + public String getDeleteOtherV() { + return this.getProp(EdgeProperty.DELETE_OTHER_V); + } + + /** + * Sets the checks for del target. + * + * @param hasDelTarget the new checks for del target + */ + public void setDeleteOtherV(String hasDelTarget) { + this.setProp(EdgeProperty.DELETE_OTHER_V, hasDelTarget); + } + + /** + * Gets the service infrastructure. + * + * @return the service infrastructure + */ + public String getServiceInfrastructure() { + return this.getProp(EdgeProperty.SVC_INFRA); + } + + /** + * Sets the service infrastructure. + * + * @param serviceInfrastructure the new service infrastructure + */ + public void setServiceInfrastructure(String serviceInfrastructure) { + this.setProp(EdgeProperty.SVC_INFRA, serviceInfrastructure); + } + + public String getPreventDelete() { + return this.getProp(EdgeProperty.PREVENT_DELETE); + } + + public void setPreventDelete(String preventDelete) { + this.setProp(EdgeProperty.PREVENT_DELETE, preventDelete); + } + + /** + * Gets the edge properties. + * + * @return the edge properties + */ + public Map<EdgeProperty, String> getEdgeProperties() { + return this.edgeProperties; + } + + /** + * Sets the prop. + * + * @param key the key + * @param value the value + */ + private void setProp(EdgeProperty key, String value) { + this.edgeProperties.put(key, value); + } + + /** + * Gets the prop. + * + * @param key the key + * @return the prop + */ + private String getProp(EdgeProperty key) { + return this.edgeProperties.get(key); + } + + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java new file mode 100644 index 00000000..d9dfa457 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java @@ -0,0 +1,589 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +import static com.jayway.jsonpath.Criteria.where; +import static com.jayway.jsonpath.Filter.filter; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Scanner; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +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.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Version; +import org.onap.aai.serialization.db.exceptions.EdgeMultiplicityException; +import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.Filter; +import com.jayway.jsonpath.JsonPath; + +public class EdgeRules { + + private EELFLogger logger = EELFManager.getInstance().getLogger(EdgeRules.class); + + private DocumentContext rulesDoc; + + /** + * Loads the most recent DbEdgeRules json file for later parsing. + * Only need most recent version for actual A&AI operations that call this class; + * the old ones are only used in tests. + */ + private EdgeRules() { + + String json = this.getEdgeRuleJson(Version.getLatest()); + rulesDoc = JsonPath.parse(json); + + } + + private EdgeRules(String rulesFilename) { + String json = this.getEdgeRuleJson(rulesFilename); + rulesDoc = JsonPath.parse(json); + } + + private String getEdgeRuleJson(String rulesFilename) { + InputStream is = getClass().getResourceAsStream(rulesFilename); + + Scanner scanner = new Scanner(is); + String json = scanner.useDelimiter("\\Z").next(); + scanner.close(); + + return json; + } + + /** + * Loads the versioned DbEdgeRules json file for later parsing. + */ + @SuppressWarnings("unchecked") + private EdgeRules(Version version) { + String json = this.getEdgeRuleJson(version); + rulesDoc = JsonPath.parse(json); + } + + private String getEdgeRuleJson(Version version) { + InputStream is = getClass().getResourceAsStream("/dbedgerules/DbEdgeRules_" + version.toString() + ".json"); + + Scanner scanner = new Scanner(is); + String json = scanner.useDelimiter("\\Z").next(); + scanner.close(); + + return json; + } + + private static class Helper { + private static final EdgeRules INSTANCE = new EdgeRules(); + private static final Map<Version, EdgeRules> INSTANCEMAP = new ConcurrentHashMap<>(); + + private static EdgeRules getEdgeRulesByFilename(String rulesFilename) { + return new EdgeRules(rulesFilename); + } + + private static EdgeRules getVersionedEdgeRules(Version v) { + if (Version.isLatest(v)) { + return INSTANCE; + } + if (!INSTANCEMAP.containsKey(v)) { + INSTANCEMAP.put(v, new EdgeRules(v)); + } + return INSTANCEMAP.get(v); + } + } + + /** + * Gets the single instance of EdgeRules. + * + * @return single instance of EdgeRules + */ + public static EdgeRules getInstance() { + return Helper.INSTANCE; + + } + + /** + * Gets the versioned instance of EdgeRules. + * + * @return versioned instance of EdgeRules + */ + public static EdgeRules getInstance(Version v) { + return Helper.getVersionedEdgeRules(v); + + } + + /** + * Loads edge rules from the given file. + * + * @param rulesFilename - name of the file to load rules from + * @return the EdgeRules instance + */ + public static EdgeRules getInstance(String rulesFilename) { + return Helper.getEdgeRulesByFilename(rulesFilename); + } + + /** + * Adds the tree edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + */ + public Edge addTreeEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, false); + } + + /** + * Adds the edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + */ + public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, false); + } + + /** + * Adds the tree edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + */ + public Edge addTreeEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, true); + } + + /** + * Adds the edge. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edge + * @throws AAIException the AAI exception + */ + public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, true); + } + + /** + * 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 + */ + private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, boolean isBestEffort) throws AAIException { + + EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex); + + Edge e = null; + + Optional<String> message = this.validateMultiplicity(rule, traversalSource, aVertex, bVertex); + + if (message.isPresent() && !isBestEffort) { + throw new EdgeMultiplicityException(message.get()); + } + if (!message.isPresent()) { + 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<EdgeProperty, String> propMap = rule.getEdgeProperties(); + + for (Entry<EdgeProperty, String> entry : propMap.entrySet()) { + edge.property(entry.getKey().toString(), entry.getValue()); + } + } + + /** + * Checks if any edge rules exist between the two given nodes, in either A|B or B|A order. + * + * @param nodeA - node at one end of the edge + * @param nodeB - node at the other end + * @return true, if any such rules exist + */ + public boolean hasEdgeRule(String nodeA, String nodeB) { + Filter aToB = filter( + where("from").is(nodeA).and("to").is(nodeB) + ); + Filter bToA = filter( + where("from").is(nodeB).and("to").is(nodeA) + ); + + List<Map<String, String>> results = readRules(aToB); + results.addAll(readRules(bToA)); + + return !results.isEmpty(); + + } + + /** + * Checks if any edge rules exist between the two given nodes, in either A|B or B|A order. + * + * @param aVertex - node at one end of the edge + * @param bVertex - node at the other end + * @return true, if any such rules exist + */ + public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex) { + String outType = aVertex.<String>property("aai-node-type").orElse(null); + String inType = bVertex.<String>property("aai-node-type").orElse(null); + + return this.hasEdgeRule(outType, inType); + + } + + /** + * Gets all the edge rules that exist between the given node types. + * The rules will be phrased in terms of out|in, though this will + * also find rules defined as in|out (it will flip the direction in + * the EdgeRule object returned accordingly to match out|in). + * + * @param outType + * @param inType + * @return Map<String edgeLabel, EdgeRule rule> where edgeLabel is the label name + * @throws AAIException + */ + 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 of the given type that exists between A and B. + * Will check B|A as well, and flips the direction accordingly if that succeeds + * to match the expected A|B return. + * + * @param type - the type of edge you're looking for + * @param nodeA - first node type + * @param nodeB - second node type + * @return EdgeRule describing the rule in terms of A|B, if there is any such rule + * @throws AAIException if no such edge exists + */ + public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB) throws AAIException { + //try A to B + List<Map<String, String>> aToBEdges = readRules(buildFilter(type, nodeA, nodeB)); + if (!aToBEdges.isEmpty()) { + //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 + verifyRule(aToBEdges.get(0)); + return buildRule(aToBEdges.get(0)); + } + + //we get here if there was nothing for A to B, so let's try B to A + List<Map<String, String>> bToAEdges = readRules(buildFilter(type, nodeB, nodeA)); + if (!bToAEdges.isEmpty()) { + verifyRule(bToAEdges.get(0)); + return flipDirection(buildRule(bToAEdges.get(0))); //bc we need to return as A|B, so flip the direction to match + } + + //found none + throw new NoEdgeRuleFoundException("no " + type.toString() + " edge between " + nodeA + " and " + nodeB); + } + + /** + * Builds a JsonPath filter to search for an edge from nodeA to nodeB with the given edge type (cousin or parent/child) + * + * @param type + * @param nodeA - start node + * @param nodeB - end node + * @return + */ + private Filter buildFilter(EdgeType type, String nodeA, String nodeB) { + if (EdgeType.COUSIN.equals(type)) { + return filter( + where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()) + ); + } else { + return filter( + where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is("${direction}")).or( + where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is("!${direction}") + ); + } + } + + /** + * Puts the give edge rule information into an EdgeRule object. + * + * @param edge - the edge information returned from JsonPath + * @return EdgeRule containing that information + */ + private EdgeRule buildRule(Map<String, String> map) { + Map<String, String> edge = new EdgePropertyMap<>(); + edge.putAll(map); + + EdgeRule rule = new EdgeRule(); + rule.setLabel(edge.get("label")); + rule.setDirection(edge.get("direction")); + rule.setMultiplicityRule(edge.get("multiplicity")); + rule.setContains(edge.get(EdgeProperty.CONTAINS.toString())); + rule.setDeleteOtherV(edge.get(EdgeProperty.DELETE_OTHER_V.toString())); + rule.setServiceInfrastructure(edge.get(EdgeProperty.SVC_INFRA.toString())); + rule.setPreventDelete(edge.get(EdgeProperty.PREVENT_DELETE.toString())); + + return rule; + } + + /** + * If getEdgeRule gets a request for A|B, and it finds something as B|A, the caller still expects + * the returned EdgeRule to reflect A|B directionality. This helper method flips B|A direction to + * match this expectation. + * + * @param rule whose direction needs flipped + * @return the updated rule + */ + private EdgeRule flipDirection(EdgeRule rule) { + if (Direction.IN.equals(rule.getDirection())) { + rule.setDirection(Direction.OUT); + return rule; + } else if (Direction.OUT.equals(rule.getDirection())) { + rule.setDirection(Direction.IN); + return rule; + } else { //direction is BOTH, flipping both is still both + return rule; + } + } + + /** + * Gets the edge rule of the given type that exists between A and B. + * Will check B|A as well, and flips the direction accordingly if that succeeds + * to match the expected A|B return. + * + * @param type - the type of edge you're looking for + * @param aVertex - first node type + * @param bVertex - second node type + * @return EdgeRule describing the rule in terms of A|B, if there is any such rule + * @throws AAIException if no such edge exists + */ + public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + + return this.getEdgeRule(type, outType, inType); + + + } + + /** + * 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 Optional<String> validateMultiplicity(EdgeRule rule, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) { + + 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 (!"".equals(detail)) { + return Optional.of(detail); + } else { + return Optional.empty(); + } + + + } + + /** + * Verifies that all required properties are defined in the given edge rule. + * If they are not, throws a RuntimeException. + * + * @param rule - Map<String edge property, String edge property value> representing + * an edge rule + */ + private void verifyRule(Map<String, String> rule) { + for (EdgeProperty prop : EdgeProperty.values()) { + if (!rule.containsKey(prop.toString())) { + /* Throws RuntimeException as rule definition errors + * cannot be recovered from, and should never happen anyway + * because these are configuration files, so requiring all + * downstream code to check for this exception seems inappropriate. + * It's instantiated with an AAIException to make sure all + * relevant information is present in the error message. + */ + throw new RuntimeException(new AAIException("AAI_4005", + "Rule between " + rule.get("from") + " and " + rule.get("to") + + " is missing property " + prop + ".")); + } + } + } + + /** + * Reads all the edge rules from the loaded json file. + * + * @return List<Map<String edge property, String edge property value>> + * Each map represents a rule read from the json. + */ + private List<Map<String, String>> readRules() { + return readRules(null); + } + + /** + * Reads the edge rules from the loaded json file, using the given filter + * to get specific rules. If filter is null, will get all rules. + * + * @param filter - may be null to indicate get all + * @return List<Map<String edge property, String edge property value>> + * Each map represents a rule read from the json. + */ + private List<Map<String, String>> readRules(Filter filter) { + List<Map<String, String>> results; + if (filter == null) { //no filter means get all + results = rulesDoc.read("$.rules.*"); + } else { + results = rulesDoc.read("$.rules.[?]", filter); + } + for (Map<String, String> result : results) { + verifyRule(result); + } + return results; + } + + /** + * Gets all the edge rules we define. + * + * @return Multimap<String "from|to", EdgeRule rule> + */ + public Multimap<String, EdgeRule> getAllRules() { + Multimap<String, EdgeRule> result = ArrayListMultimap.create(); + + List<Map<String, String>> rules = readRules(); + for (Map<String, String> rule : rules) { + EdgeRule er = buildRule(rule); + String name = rule.get("from") + "|" + rule.get("to"); + result.put(name, er); + } + + return result; + } + + /** + * Gets all edge rules that define a child relationship from + * the given node type. + * + * @param nodeType + * @return + */ + public Set<EdgeRule> getChildren(String nodeType) { + + final Filter filter = filter( + where("from").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is("${direction}") + ).or(where("to").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is("!${direction}")); + + final List<Map<String, String>> rules = readRules(filter); + final Set<EdgeRule> result = new HashSet<>(); + rules.forEach(item -> { + verifyRule(item); + result.add(buildRule(item)); + }); + + return result; + + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeType.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeType.java new file mode 100644 index 00000000..088e13bb --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeType.java @@ -0,0 +1,27 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +public enum EdgeType { + COUSIN, + TREE; +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/GetAllPool.java b/aai-core/src/main/java/org/onap/aai/serialization/db/GetAllPool.java new file mode 100644 index 00000000..4d38bd38 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/GetAllPool.java @@ -0,0 +1,47 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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/onap/aai/serialization/db/GraphSingleton.java b/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java new file mode 100644 index 00000000..9bb04d6b --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/GraphSingleton.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +import com.thinkaurelius.titan.core.TitanGraph; +import org.onap.aai.dbmap.AAIGraph; +import org.onap.aai.dbmap.DBConnectionType; + +import java.util.concurrent.atomic.AtomicInteger; + +/* 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/onap/aai/serialization/db/LegacyDBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/LegacyDBSerializer.java new file mode 100644 index 00000000..b9343816 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/LegacyDBSerializer.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; + +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/onap/aai/serialization/db/MultiplicityRule.java b/aai-core/src/main/java/org/onap/aai/serialization/db/MultiplicityRule.java new file mode 100644 index 00000000..0451c0d7 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/MultiplicityRule.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db; + +public enum MultiplicityRule { + MANY2ONE, + ONE2MANY, + ONE2ONE, + MANY2MANY +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/TitanGraphSingleton.java b/aai-core/src/main/java/org/onap/aai/serialization/db/TitanGraphSingleton.java new file mode 100644 index 00000000..e1accb4e --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/TitanGraphSingleton.java @@ -0,0 +1,39 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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/onap/aai/serialization/db/exceptions/EdgeMultiplicityException.java b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/EdgeMultiplicityException.java new file mode 100644 index 00000000..6baa113d --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/EdgeMultiplicityException.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db.exceptions; + +import org.onap.aai.exceptions.AAIException; + +public class EdgeMultiplicityException extends AAIException { + + private static final long serialVersionUID = -5575661036426538012L; + + public EdgeMultiplicityException(String message) { + super("AAI_6140", message); + } + + public EdgeMultiplicityException(Throwable cause) { + super("AAI_6140",cause); + } + + public EdgeMultiplicityException(String message, Throwable cause) { + super("AAI_6140", cause, message); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java new file mode 100644 index 00000000..247379cb --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db.exceptions; + +import org.onap.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/onap/aai/serialization/engines/QueryStyle.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java new file mode 100644 index 00000000..db947bf8 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/QueryStyle.java @@ -0,0 +1,26 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.engines; + +public enum QueryStyle { + GREMLIN_TRAVERSAL, GREMLIN_UNIQUE, GREMLINPIPELINE_TRAVERSAL, TRAVERSAL +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/TitanDBEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/TitanDBEngine.java new file mode 100644 index 00000000..315081e7 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/TitanDBEngine.java @@ -0,0 +1,102 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.introspection.Loader; +import org.onap.aai.serialization.db.TitanGraphSingleton; + +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/onap/aai/serialization/engines/TransactionalGraphEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java new file mode 100644 index 00000000..11bf5955 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/TransactionalGraphEngine.java @@ -0,0 +1,246 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.introspection.Loader; +import org.onap.aai.query.builder.GremlinTraversal; +import org.onap.aai.query.builder.GremlinUnique; +import org.onap.aai.query.builder.QueryBuilder; +import org.onap.aai.query.builder.TraversalQuery; +import org.onap.aai.serialization.db.GraphSingleton; +import org.onap.aai.serialization.engines.query.GraphTraversalQueryEngine; +import org.onap.aai.serialization.engines.query.QueryEngine; + +import com.thinkaurelius.titan.core.TitanGraph; +import com.thinkaurelius.titan.core.schema.TitanManagement; + +public abstract class TransactionalGraphEngine { + + protected GraphSingleton singleton = null; + protected QueryEngine queryEngine = null; + protected QueryBuilder<Vertex> queryBuilder = null; + protected QueryStyle style = null; + protected final DBConnectionType connectionType; + protected final Loader loader; + protected Graph 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<Vertex> getQueryBuilder() { + return getQueryBuilder(this.loader); + } + + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style) { + return getQueryBuilder(style, this.loader); + } + + public QueryBuilder<Vertex> getQueryBuilder(Loader loader) { + return getQueryBuilder(this.style, loader); + } + + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style, 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<Vertex> getQueryBuilder(Vertex start) { + return getQueryBuilder(this.loader, start); + } + + public QueryBuilder<Vertex> getQueryBuilder(Loader loader, Vertex start) { + return getQueryBuilder(this.style, loader, start); + } + + public QueryBuilder<Vertex> getQueryBuilder(QueryStyle style, 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 Graph 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().tx().rollback(); + this.currentTx = null; + this.currentTraversal = null; + this.readOnlyTraversal = null; + } + } + public void commit() { + if (this.tx() != null) { + this.tx().tx().commit(); + this.currentTx = null; + this.currentTraversal = null; + this.readOnlyTraversal = null; + } + } + + public Graph 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/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java new file mode 100644 index 00000000..872b0c5f --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java @@ -0,0 +1,198 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.engines.query; + + +import static org.onap.aai.serialization.db.AAIDirection.IN; +import static org.onap.aai.serialization.db.AAIDirection.NONE; +import static org.onap.aai.serialization.db.AAIDirection.OUT; +import static org.onap.aai.serialization.db.EdgeProperty.CONTAINS; +import static org.onap.aai.serialization.db.EdgeProperty.DELETE_OTHER_V; + +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.onap.aai.db.props.AAIProperties; +import org.onap.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(__.union(__.inE().has(CONTAINS.toString(), OUT.toString()).outV(), __.outE().has(CONTAINS.toString(), IN.toString()).inV())); + return pipe.toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findAllChildren(Vertex start) { + + GraphTraversal<Vertex, Vertex> pipe = this.g + .V(start).emit(v -> true).repeat(__.union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(), __.inE().has(CONTAINS.toString(), IN.toString()).outV())); + + + return pipe.toList(); + + } + + public List<Vertex> findChildrenOfType(Vertex start, String type) { + GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).union( + __.outE().has(CONTAINS.toString(), OUT.toString()).inV(), + __.inE().has(CONTAINS.toString(), IN.toString()).outV() + ).has(AAIProperties.NODE_TYPE, type).dedup(); + + return pipe.toList(); + } + + public List<Vertex> findChildren(Vertex start) { + GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).union( + __.outE().has(CONTAINS.toString(), OUT.toString()), + __.inE().has(CONTAINS.toString(), IN.toString()) + ).otherV().dedup(); + + return pipe.toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findDeletable(Vertex start) { + GraphTraversal<Vertex, Vertex> pipe = this.g + .V(start).emit(v -> true).repeat( + __.union( + __.outE().or( + __.has(CONTAINS.toString(), OUT.toString()), + __.has(DELETE_OTHER_V.toString(), OUT.toString()) + ).inV(), + __.inE().or( + __.has(CONTAINS.toString(), IN.toString()), + __.has(DELETE_OTHER_V.toString(), IN.toString()) + ).outV() + ) + ); + + 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( + __.union( + __.outE().has(CONTAINS.toString(), OUT.toString()).inV(), + __.inE().has(CONTAINS.toString(), IN.toString()).outV()) + ); + + if (!nodeOnly) { + t.union( + __.identity(), + __.bothE().has(CONTAINS.toString(), NONE.toString()).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(CONTAINS.toString(), NONE.toString()).where(__.outV().has(AAIProperties.NODE_TYPE, P.within(objects))), + __.outE().has(CONTAINS.toString(), NONE.toString()).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(CONTAINS.toString(), NONE.toString()), + __.outE().has(CONTAINS.toString(), NONE.toString())).otherV().dedup(); + + return pipeline.toList(); + } + +} + diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GremlinPipelineQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GremlinPipelineQueryEngine.java new file mode 100644 index 00000000..e8acaecd --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GremlinPipelineQueryEngine.java @@ -0,0 +1,206 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.engines.query;/*- + * ============LICENSE_START======================================================= + * org.onap.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.onap.aai.serialization.engines.query; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.onap.aai.db.AAIProperties; +import org.onap.aai.query.builder.QueryBuilder; +import org.onap.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/onap/aai/serialization/engines/query/GremlinQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GremlinQueryEngine.java new file mode 100644 index 00000000..ab622963 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GremlinQueryEngine.java @@ -0,0 +1,196 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.engines.query;/*- + * ============LICENSE_START======================================================= + * org.onap.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.onap.aai.serialization.engines.query; +// +//import java.util.List; +//import java.util.regex.Matcher; +//import java.util.regex.Pattern; +// +//import org.apache.commons.collections.IteratorUtils; +// +//import org.onap.aai.db.AAIProperties; +//import org.onap.aai.query.builder.QueryBuilder; +//import org.onap.aai.serialization.engines.TransactionalGraphEngine; +//import com.tinkerpop.blueprints.Direction; +//import com.tinkerpop.blueprints.Graph; +//import com.tinkerpop.blueprints.TransactionalGraph; +//import com.tinkerpop.blueprints.Vertex; +//import com.tinkerpop.gremlin.groovy.Gremlin; +//import com.tinkerpop.gremlin.java.GremlinPipeline; +//import com.tinkerpop.pipes.Pipe; +//import com.tinkerpop.pipes.util.iterators.SingleIterator; +// +//public class GremlinQueryEngine extends QueryEngine { +// +// public GremlinQueryEngine (TransactionalGraphEngine engine) { +// super(engine); +// +// } +// +// +// @Override +// public List<Vertex> executeQuery(TransactionalGraph g, QueryBuilder query) { +// List<Vertex> result = null; +// Vertex start = query.getStart(); +// if (start != null) { +// result = this.executeQuery(start, (String)query.getQuery()); +// } else { +// result = this.processGremlinQuery((String)query.getQuery()); +// } +// return result; +// +// } +// +// @Override +// public List<Vertex> executeParentQuery(TransactionalGraph g, QueryBuilder query) { +// +// List<Vertex> result = null; +// Vertex start = query.getStart(); +// if (start != null) { +// result = this.executeQuery(start, (String)query.getParentQuery()); +// } else { +// result = this.processGremlinQuery((String)query.getParentQuery()); +// } +// return result; +// } +// +// private List<Vertex> executeQuery(Vertex startVertex, String query) { +// +// return this.processGremlinQuery(startVertex, "_()" + query); +// +// } +// +// @Override +// public List<Vertex> findParents(Vertex start) { +// +// String findAllParents = ".as('x').inE.has('isParent', true).outV" +// + ".loop('x'){it.object.inE.has('isParent',true).count()==1}{true}"; +// +// List<Vertex> results = this.executeQuery(start, findAllParents); +// results.add(0, start); +// return results; +// +// } +// +// @Override +// public List<Vertex> findChildren(Vertex start) { +// String findAllChildren = ".as('x').outE.has('isParent', true).inV" +// + ".loop('x'){it.object.outE.has('isParent', true).count() >= 1}{true}"; +// +// List<Vertex> results = this.executeQuery(start, findAllChildren); +// results.add(0, start); +// return results; +// +// } +// +// @Override +// public List<Vertex> findDeletable(Vertex start) { +// String findAllChildren = ".as('x').outE.or(_().has('isParent', true), _().has('hasDelTarget', true)).inV" +// + ".loop('x'){it.object.outE.or(_().has('isParent', true), _().has('hasDelTarget', true)).count() >= 1}{true}"; +// +// List<Vertex> results = this.executeQuery(start, findAllChildren); +// results.add(0, start); +// return results; +// } +// private List<Vertex> processGremlinQuery(String query) { +// +// Pattern firstHasSet = Pattern.compile("^(\\.has\\(.*?\\))(\\.has\\(.*?\\))*(?!\\.has)"); +// Pattern p = Pattern.compile("\\.has\\('(.*?)',\\s?'(.*?)'\\)"); +// Matcher m = firstHasSet.matcher(query); +// List<Vertex> results = null; +// GremlinPipeline<Graph, Vertex> pipe = new GremlinPipeline<>(dbEngine.getGraph()); +// if (m.find()) { +// String hasSet = m.group(); +// query = query.replace(m.group(0), ""); +// m = p.matcher(hasSet); +// pipe.V(); +// while (m.find()) { +// pipe.has(m.group(1), m.group(2)); +// } +// results = processGremlinQuery(pipe.toList(), "_()" + query); +// } +// +// return results; +// +// } +// private List<Vertex> processGremlinQuery(Vertex startVertex, String query) { +// +// Pipe pipe = Gremlin.compile(query); +// pipe.setStarts(new SingleIterator<Vertex>(startVertex)); +// +// return (List<Vertex>)IteratorUtils.toList(pipe.iterator()); +// } +// private List<Vertex> processGremlinQuery(List<Vertex> list, String query) { +// +// Pipe pipe = Gremlin.compile(query); +// +// pipe.setStarts(list); +// +// return (List<Vertex>)IteratorUtils.toList(pipe.iterator()); +// } +// +// +// @Override +// public List<Vertex> findRelatedVertices(Vertex start, Direction direction, String label, String nodeType) { +// String findRelatedVertices = "_()"; +// switch (direction) { +// case OUT: +// findRelatedVertices += ".out('" + label + "')"; +// break; +// case IN: +// findRelatedVertices += ".in('" + label + "')"; +// break; +// case BOTH: +// findRelatedVertices += ".both('" + label + "')"; +// break; +// default: +// break; +// } +// findRelatedVertices += ".has('" + AAIProperties.NODE_TYPE + "', '" + nodeType + "').dedup()"; +// List<Vertex> results = this.executeQuery(start, findRelatedVertices); +// results.add(0, start); +// return results; +// } +// +//} +// diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java new file mode 100644 index 00000000..01f11b17 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.engines.query; + +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.onap.aai.db.props.AAIProperties; +import org.onap.aai.introspection.Loader; + +import java.util.List; + +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); + + public abstract List<Vertex> findChildren(Vertex start); + /** + * 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/onap/aai/serialization/queryformats/Console.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java new file mode 100644 index 00000000..f101f890 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Console.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import com.google.gson.JsonObject; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +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/onap/aai/serialization/queryformats/Format.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java new file mode 100644 index 00000000..2904cce6 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +public enum Format { + graphson, + pathed, id, resource, + simple, + resource_and_url, + console, + raw +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java new file mode 100644 index 00000000..b19e0c3e --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java @@ -0,0 +1,97 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Loader; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.QueryParamInjectionException; +import org.onap.aai.serialization.queryformats.utils.QueryParamInjector; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + +public class FormatFactory { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + private final QueryParamInjector injector; + public FormatFactory (Loader loader, DBSerializer serializer) throws AAIException { + this.loader = loader; + this.serializer = serializer; + this.urlBuilder = new UrlBuilder(loader.getVersion(), serializer); + this.injector = QueryParamInjector.getInstance(); + } + + public Formatter get(Format format) throws AAIException { + return get(format, new MultivaluedHashMap<String, String>()); + } + + public Formatter get(Format format, MultivaluedMap<String, String> params) throws AAIException { + + Formatter formattter = null; + + switch (format) { + case graphson : + formattter = new Formatter(inject(new GraphSON(), params)); + break; + case pathed : + formattter = new Formatter(inject(new PathedURL(loader, urlBuilder), params)); + break; + case id : + formattter = new Formatter(inject(new IdURL(loader, urlBuilder), params)); + break; + case resource : + formattter = new Formatter(inject(new Resource.Builder(loader, serializer, urlBuilder), params).build()); + break; + case resource_and_url : + formattter = new Formatter(inject(new Resource.Builder(loader, serializer, urlBuilder).includeUrl(), params).build()); + break; + case raw : + formattter = new Formatter(inject(new RawFormat.Builder(loader, serializer, urlBuilder), params).build()); + break; + case simple : + formattter = new Formatter(inject(new RawFormat.Builder(loader, serializer, urlBuilder).depth(0).modelDriven(), params).build()); + break; + case console : + formattter = new Formatter(inject(new Console(), params)); + break; + default : + break; + + } + + return formattter; + } + + private <T> T inject (T obj, MultivaluedMap<String, String> params) throws QueryParamInjectionException { + + injector.injectParams(obj, params); + return obj; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java new file mode 100644 index 00000000..fb822dd9 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatMapper.java @@ -0,0 +1,32 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import com.google.gson.JsonObject; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +public interface FormatMapper { + + public JsonObject formatObject(Object v) throws AAIFormatVertexException; + + public int parallelThreshold(); +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java new file mode 100644 index 00000000..50042b76 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Formatter.java @@ -0,0 +1,80 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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.onap.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(); + } + final boolean isParallel = stream.isParallel(); + 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()) { + if (isParallel) { + synchronized (body) { + body.add(obj.get()); + } + } else { + body.add(obj.get()); + } + } + }); + + result.add("results", body); + + return result.getAsJsonObject(); + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java new file mode 100644 index 00000000..00aa781c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/GraphSON.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.thinkaurelius.titan.graphdb.tinkerpop.TitanIoRegistry; +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 java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +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/onap/aai/serialization/queryformats/IdURL.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java new file mode 100644 index 00000000..320cd616 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/IdURL.java @@ -0,0 +1,71 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +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/onap/aai/serialization/queryformats/PathedURL.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java new file mode 100644 index 00000000..8b9d5058 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/PathedURL.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +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/onap/aai/serialization/queryformats/RawFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java new file mode 100644 index 00000000..7aaf3035 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java @@ -0,0 +1,190 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import java.util.Iterator; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.introspection.Loader; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.NodesOnly; +import org.onap.aai.serialization.queryformats.utils.UrlBuilder; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + + +public class RawFormat implements FormatMapper { + protected JsonParser parser = new JsonParser(); + protected final DBSerializer serializer; + protected final Loader loader; + protected final UrlBuilder urlBuilder; + protected final int depth; + protected final boolean nodesOnly; + protected RawFormat(Builder builder) { + this.urlBuilder = builder.getUrlBuilder(); + this.loader = builder.getLoader(); + this.serializer = builder.getSerializer(); + this.depth = builder.getDepth(); + this.nodesOnly = builder.isNodesOnly(); + } + + @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)); + if (!nodesOnly) { + json.add("related-to", this.createRelationshipObject(v)); + } + return json; + } + + @Override + public int parallelThreshold() { + return 100; + } + + + public JsonObject createPropertiesObject(Vertex v) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + Iterator<VertexProperty<Object>> iter = v.properties(); + + while (iter.hasNext()) { + VertexProperty<Object> prop = iter.next(); + 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; + } + protected 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; + } + + public static class Builder implements NodesOnly<Builder>, Depth<Builder> { + + protected final Loader loader; + protected final DBSerializer serializer; + protected final UrlBuilder urlBuilder; + protected boolean includeUrl = false; + protected boolean nodesOnly = false; + protected int depth = 1; + protected boolean modelDriven = 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; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + public boolean isNodesOnly() { + return this.nodesOnly; + } + + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + + public int getDepth() { + return this.depth; + } + + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public Builder modelDriven() { + this.modelDriven = true; + return this; + } + + public boolean getModelDriven() { + return this.modelDriven; + } + public RawFormat build() { + if (modelDriven) { + return new SimpleFormat(this); + } else { + return new RawFormat(this); + } + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java new file mode 100644 index 00000000..649971be --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/Resource.java @@ -0,0 +1,163 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.serialization.queryformats.params.Depth; +import org.onap.aai.serialization.queryformats.params.NodesOnly; +import org.onap.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 final boolean nodesOnly; + private final int depth; + private Resource (Builder builder) { + this.parser = new JsonParser(); + this.loader = builder.getLoader(); + this.serializer = builder.getSerializer(); + this.urlBuilder = builder.getUrlBuilder(); + this.includeUrl = builder.isIncludeUrl(); + this.nodesOnly = builder.isNodesOnly(); + this.depth = builder.getDepth(); + } + + @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, this.depth, this.nodesOnly, "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 implements NodesOnly<Builder>, Depth<Builder> { + + private final Loader loader; + private final DBSerializer serializer; + private final UrlBuilder urlBuilder; + private boolean includeUrl = false; + private boolean nodesOnly = false; + private int depth = 1; + 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; + } + + public Builder nodesOnly(Boolean nodesOnly) { + this.nodesOnly = nodesOnly; + return this; + } + public boolean isNodesOnly() { + return this.nodesOnly; + } + public Builder depth(Integer depth) { + this.depth = depth; + return this; + } + public int getDepth() { + return this.depth; + } + public boolean isIncludeUrl() { + return this.includeUrl; + } + + public Resource build() { + return new Resource(this); + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SimpleFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SimpleFormat.java new file mode 100644 index 00000000..ba49e3a3 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SimpleFormat.java @@ -0,0 +1,76 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; + +import com.google.gson.JsonObject; + +public class SimpleFormat extends RawFormat { + + + protected SimpleFormat(Builder builder) { + super(builder); + + } + + @Override + public int parallelThreshold() { + return 20; + } + + @Override + public JsonObject createPropertiesObject(Vertex v) throws AAIFormatVertexException { + try { + final Introspector obj = loader.introspectorFromName( + v.<String>property(AAIProperties.NODE_TYPE) + .orElse(null) + ); + + final List<Vertex> wrapper = new ArrayList<>(); + + wrapper.add(v); + + try { + serializer.dbToObject(wrapper, obj, this.depth, true, "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 parser.parse(json).getAsJsonObject(); + } catch (AAIUnknownObjectException e) { + throw new AAIFormatVertexException("Failed to format vertex - unknown object", e); + } + + + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SubGraphStyle.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SubGraphStyle.java new file mode 100644 index 00000000..6a8acd2f --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/SubGraphStyle.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats; + +public enum SubGraphStyle { + star, + prune, + no_op +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java new file mode 100644 index 00000000..98b90050 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatVertexException.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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/onap/aai/serialization/queryformats/exceptions/QueryParamInjectionException.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/QueryParamInjectionException.java new file mode 100644 index 00000000..313b8942 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/QueryParamInjectionException.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.exceptions; + +import org.onap.aai.exceptions.AAIException; + +public class QueryParamInjectionException extends AAIException { + + private static final long serialVersionUID = -5575661036426538012L; + + public QueryParamInjectionException(String message) { + super("AAI_4017", message); + } + + public QueryParamInjectionException(Throwable cause) { + super("AAI_4017",cause); + } + + public QueryParamInjectionException(String message, Throwable cause) { + super("AAI_4017", cause, message); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Depth.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Depth.java new file mode 100644 index 00000000..e98f0667 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Depth.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.params; + +@Inject(name = "depth") +public interface Depth<T> { + + @Setter + public T depth(Integer depth); +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Inject.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Inject.java new file mode 100644 index 00000000..18d85545 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Inject.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.params; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Inject { + + /** + * The way the query parameter appears in the URI + * @return + */ + String name(); +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/NodesOnly.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/NodesOnly.java new file mode 100644 index 00000000..1429d552 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/NodesOnly.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.params; + +@Inject(name = "nodesOnly") +public interface NodesOnly<T> { + + @Setter + public T nodesOnly(Boolean nodesOnly); +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Setter.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Setter.java new file mode 100644 index 00000000..e95de0db --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/params/Setter.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.params; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +/** + * Marks what method in the object should be used for setting the value + * of the query parameter + */ +public @interface Setter { + +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java new file mode 100644 index 00000000..29fb56df --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/QueryParamInjector.java @@ -0,0 +1,85 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.queryformats.utils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +import javax.ws.rs.core.MultivaluedMap; + +import org.onap.aai.serialization.queryformats.exceptions.QueryParamInjectionException; +import org.onap.aai.serialization.queryformats.params.Inject; +import org.onap.aai.serialization.queryformats.params.Setter; +import org.reflections.Reflections; + +public class QueryParamInjector { + + private final Set<Class<?>> results; + + + private QueryParamInjector () { + Reflections reflections = new Reflections("org.onap.aai.serialization.queryformats.params"); + results = reflections.getTypesAnnotatedWith(Inject.class); + } + + private static class Helper { + private static final QueryParamInjector INSTANCE = new QueryParamInjector(); + } + + public static QueryParamInjector getInstance() { + return Helper.INSTANCE; + } + + public <T> T injectParams(T obj, MultivaluedMap<String, String> params) throws QueryParamInjectionException{ + try { + for (Class<?> item : results) { + if (item.isAssignableFrom(obj.getClass())) { + String name = item.getAnnotation(Inject.class).name(); + + if (params.containsKey(name)) { + String value = params.getFirst(name); + + for (Method method : item.getMethods()) { + if (method.isAnnotationPresent(Setter.class)) { + Class<?>[] args = method.getParameterTypes(); + if (args.length == 1) { + Object o = args[0].getConstructor(String.class).newInstance(value); + method.invoke(obj, o); + } else { + method.invoke(obj); + } + } + } + } + } + } + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException + | SecurityException e) { + throw new QueryParamInjectionException("issue with query params", e); + } + + + return obj; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/UrlBuilder.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/UrlBuilder.java new file mode 100644 index 00000000..8387285d --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/utils/UrlBuilder.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.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.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Version; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException; +import org.onap.aai.util.AAIApiServerURLBase; +import org.onap.aai.util.AAIConstants; +import org.onap.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 = this.getServerBase(version); + } + + public UrlBuilder (Version version, DBSerializer serializer, String serverBase) { + this.serializer = serializer; + this.version = version; + this.serverBase = serverBase; + } + + public String pathed(Vertex v) throws AAIFormatVertexException { + + try { + final StringBuilder result = new StringBuilder(); + final URI uri = this.serializer.getURIForVertex(v); + + if (this.version.compareTo(Version.v11) >= 0) { + result.append(AAIConstants.AAI_APP_ROOT); + } else { + result.append(this.serverBase); + } + result.append(this.version); + result.append(uri); + + 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); + if (this.version.compareTo(Version.v11) >= 0) { + result.insert(0, AAIConstants.AAI_APP_ROOT); + } else { + result.insert(0, this.serverBase); + } + + return result.toString(); + } + + protected String getServerBase(Version v) throws AAIException { + return AAIApiServerURLBase.get(v); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/tinkerpop/TreeBackedEdge.java b/aai-core/src/main/java/org/onap/aai/serialization/tinkerpop/TreeBackedEdge.java new file mode 100644 index 00000000..82aa7443 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/tinkerpop/TreeBackedEdge.java @@ -0,0 +1,81 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.tinkerpop; + +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; + +import java.util.Iterator; + + +/** + * 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/onap/aai/serialization/tinkerpop/TreeBackedVertex.java b/aai-core/src/main/java/org/onap/aai/serialization/tinkerpop/TreeBackedVertex.java new file mode 100644 index 00000000..e953dc2d --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/tinkerpop/TreeBackedVertex.java @@ -0,0 +1,163 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.tinkerpop; + +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * 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(); + } + +} |