diff options
author | Venkata Harish K Kajur <vk250x@att.com> | 2017-05-12 17:36:59 -0400 |
---|---|---|
committer | Venkata Harish K Kajur <vk250x@att.com> | 2017-05-12 17:37:13 -0400 |
commit | b5b99b72733ca5c064b46fdf4527ace28b8b545b (patch) | |
tree | b1606e7d824e5d56b3530f724bf7bf326f9a7833 /aai-traversal/src/main/java/org/openecomp | |
parent | b6a2b26886ac329dd0836270127c1b9d283c3659 (diff) |
Add all of the traversal source files
Change-Id: Id31f4bdda9c86f782f86829f8b86dada959a9729
Signed-off-by: Venkata Harish K Kajur <vk250x@att.com>
Diffstat (limited to 'aai-traversal/src/main/java/org/openecomp')
49 files changed, 16397 insertions, 0 deletions
diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsErrorMessageLookupService.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsErrorMessageLookupService.java new file mode 100644 index 0000000..ca78bac --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsErrorMessageLookupService.java @@ -0,0 +1,98 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.ajsc_aai; + +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.WebApplicationContext; + +import ajsc.ErrorMessageLookupService; + +@Path("/errormessage") +public class JaxrsErrorMessageLookupService { + + private final static Logger logger = LoggerFactory + .getLogger(ErrorMessageLookupService.class); + + /** + * Gets the message. + * + * @param input the input + * @param errorCode the error code + * @param appId the app id + * @param operation the operation + * @param messageText the message text + * @param isRESTService the is REST service + * @param faultEntity the fault entity + * @param ConvID the conv ID + * @return the message + */ + @GET + @Path("/emls") + @Produces("text/plain") + public String getMessage(@PathParam("input") String input, + @HeaderParam("errorCode") String errorCode, + @HeaderParam("appId") String appId, + @HeaderParam("operation") String operation, + @HeaderParam("messageText") String messageText, + @HeaderParam("isRESTService") String isRESTService, + @HeaderParam("faultEntity") String faultEntity, + @HeaderParam("ConvID") String ConvID) { + + Map<String, String> headers = new HashMap<String, String>(); + headers.put(errorCode, errorCode); + headers.put(appId, appId); + headers.put(operation, operation); + headers.put(messageText, messageText); + headers.put(isRESTService, isRESTService); + headers.put(faultEntity, faultEntity); + headers.put(ConvID, ConvID); + + WebApplicationContext applicationContext = ContextLoader + .getCurrentWebApplicationContext(); + + ErrorMessageLookupService e = (ErrorMessageLookupService) applicationContext + .getBean("errorMessageLookupService"); + + String message = e.getExceptionDetails(appId, operation, errorCode, + messageText,isRESTService, faultEntity, ConvID); + + System.out.println("Error code = " + errorCode); + System.out.println("appId = " + appId); + System.out.println("operation = " + operation); + System.out.println("messageText = " + messageText); + System.out.println("isRESTService = " + isRESTService); + System.out.println("faultEntity = " + faultEntity); + System.out.println("ConvID = " + ConvID); + return "The exception message is:\n " + message; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsUserService.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsUserService.java new file mode 100644 index 0000000..39c78f6 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/JaxrsUserService.java @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.ajsc_aai; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import java.util.Map; +import java.util.HashMap; + +@Path("/user") +public class JaxrsUserService { + + private static final Map<String,String> userIdToNameMap; + static { + userIdToNameMap = new HashMap<String,String>(); + userIdToNameMap.put("userID1","Name1"); + userIdToNameMap.put("userID2","Name2"); + } + + /** + * Lookup user. + * + * @param userId the user id + * @return the string + */ + @GET + @Path("/{userId}") + @Produces("text/plain") + public String lookupUser(@PathParam("userId") String userId) { + String name = userIdToNameMap.get(userId); + return name != null ? name : "unknown id"; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesListener.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesListener.java new file mode 100644 index 0000000..22c1cb9 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesListener.java @@ -0,0 +1,19 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java new file mode 100644 index 0000000..656b290 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertiesMap.java @@ -0,0 +1,126 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.ajsc_aai.filemonitor; + +import java.io.File; +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ServicePropertiesMap +{ + private static HashMap<String, HashMap<String, String>> mapOfMaps = new HashMap<String, HashMap<String, String>>(); + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ServicePropertiesMap.class); + + /** + * Refresh. + * + * @param file the file + * @throws Exception the exception + */ + public static void refresh(File file) throws Exception + { + try + { + LOGGER.info("Loading properties - " + (file != null?file.getName():"")); + + //Store .json & .properties files into map of maps + String filePath = file.getPath(); + + if(filePath.lastIndexOf(".json")>0){ + + ObjectMapper om = new ObjectMapper(); + TypeReference<HashMap<String, String>> typeRef = new TypeReference<HashMap<String, String>>() {}; + HashMap<String, String> propMap = om.readValue(file, typeRef); + HashMap<String, String> lcasePropMap = new HashMap<String, String>(); + for (String key : propMap.keySet() ) + { + String lcaseKey = ifNullThenEmpty(key); + lcasePropMap.put(lcaseKey, propMap.get(key)); + } + + mapOfMaps.put(file.getName(), lcasePropMap); + + + }else if(filePath.lastIndexOf(".properties")>0){ + Properties prop = new Properties(); + FileInputStream fis = new FileInputStream(file); + prop.load(fis); + + @SuppressWarnings("unchecked") + HashMap<String, String> propMap = new HashMap<String, String>((Map)prop); + + mapOfMaps.put(file.getName(), propMap); + } + + LOGGER.info("File - " + file.getName() + " is loaded into the map and the corresponding system properties have been refreshed"); + } + catch (Exception e) + { + LOGGER.error("File " + (file != null?file.getName():"") + " cannot be loaded into the map ", e); + throw new Exception("Error reading map file " + (file != null?file.getName():""), e); + } + } + + /** + * Gets the property. + * + * @param fileName the file name + * @param propertyKey the property key + * @return the property + */ + public static String getProperty(String fileName, String propertyKey) + { + HashMap<String, String> propMap = mapOfMaps.get(fileName); + return propMap!=null?propMap.get(ifNullThenEmpty(propertyKey)):""; + } + + /** + * Gets the properties. + * + * @param fileName the file name + * @return the properties + */ + public static HashMap<String, String> getProperties(String fileName){ + return mapOfMaps.get(fileName); + } + + /** + * If null then empty. + * + * @param key the key + * @return the string + */ + private static String ifNullThenEmpty(String key) { + if (key == null) { + return ""; + } else { + return key; + } + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertyService.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertyService.java new file mode 100644 index 0000000..22c1cb9 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/filemonitor/ServicePropertyService.java @@ -0,0 +1,19 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ diff --git a/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/util/ServicePropertiesMapBean.java b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/util/ServicePropertiesMapBean.java new file mode 100644 index 0000000..7577de7 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/ajsc_aai/util/ServicePropertiesMapBean.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.ajsc_aai.util; + +import org.openecomp.aai.ajsc_aai.filemonitor.ServicePropertiesMap; + +public class ServicePropertiesMapBean { + + /** + * Gets the property. + * + * @param propFileName the prop file name + * @param propertyKey the property key + * @return the property + */ + public static String getProperty(String propFileName, String propertyKey) { + return ServicePropertiesMap.getProperty(propFileName, propertyKey); + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/db/DbMethHelper.java b/aai-traversal/src/main/java/org/openecomp/aai/db/DbMethHelper.java new file mode 100644 index 0000000..79172d9 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/db/DbMethHelper.java @@ -0,0 +1,158 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.db; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.parsers.query.QueryParser; +import org.openecomp.aai.parsers.relationship.RelationshipToURI; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; + +public class DbMethHelper { + + private final Loader loader; + private final TransactionalGraphEngine engine; + + protected DbMethHelper() { + this.loader = null; + this.engine = null; + } + public DbMethHelper(Loader loader, TransactionalGraphEngine engine) { + this.loader = loader; + this.engine = engine; + } + /** + * + * @param type + * @param map - form [{type}.{propname}:{value}] + * @return + * @throws UnsupportedEncodingException + * @throws AAIException + */ + public Optional<Vertex> searchVertexByIdentityMap(String type, Map<String, Object> map) throws AAIException { + + Introspector relationship = constructRelationship(type, map); + RelationshipToURI parser; + URI uri; + QueryParser queryParser; + try { + parser = new RelationshipToURI(loader, relationship); + uri = parser.getUri(); + queryParser = this.engine.getQueryBuilder().createQueryFromURI(uri); + } catch (UnsupportedEncodingException e) { + throw new AAIException("AAI_3000"); + } + + List<Vertex> results = queryParser.getQueryBuilder().toList(); + + return reduceToSingleVertex(results, map); + } + + /** + * @param type + * @param map - form [{propname}:{value}] + * @return + * @throws AAIException + */ + public Optional<Vertex> locateUniqueVertex(String type, Map<String, Object> map) throws AAIException { + + return reduceToSingleVertex(locateUniqueVertices(type, map), map); + } + + public List<Vertex> locateUniqueVertices(String type, Map<String, Object> map) throws AAIException { + Introspector obj = this.createIntrospectorFromMap(type, map); + QueryBuilder builder = this.engine.getQueryBuilder().exactMatchQuery(obj); + + return builder.toList(); + } + private Introspector constructRelationship(String type, Map<String, Object> map) throws AAIUnknownObjectException { + final Introspector relationship = loader.introspectorFromName("relationship"); + relationship.setValue("related-to", type); + final List<Object> data = relationship.getValue("relationship-data"); + for (Entry<String, Object> entry : map.entrySet()) { + final Introspector dataObj = loader.introspectorFromName("relationship-data"); + dataObj.setValue("relationship-key", entry.getKey()); + dataObj.setValue("relationship-value", entry.getValue()); + data.add(dataObj.getUnderlyingObject()); + } + + return relationship; + } + + private Introspector createIntrospectorFromMap(String targetNodeType, Map<String, Object> propHash) throws AAIUnknownObjectException { + final Introspector result = loader.introspectorFromName(targetNodeType); + for (Entry<String, Object> entry : propHash.entrySet()) { + result.setValue(entry.getKey(), entry.getValue()); + } + return result; + } + + private Optional<Vertex> reduceToSingleVertex(List<Vertex> vertices, Map<String, Object> map) throws AAIException { + if (vertices.isEmpty()){ + return Optional.empty(); + } else if (vertices.size() > 1) { + throw new AAIException("AAI_6112", "More than one Node found by getUniqueNode for params: " + map); + } + + return Optional.of(vertices.get(0)); + } + public List<String> getVertexProperties(Vertex v) { + List<String> retArr = new ArrayList<>(); + if( v == null ){ + retArr.add("null Node object passed to showPropertiesForNode()\n"); + } + else { + String nodeType; + Object ob = v.<Object>property("aai-node-type").orElse(null); + if( ob == null ){ + nodeType = "null"; + } + else{ + nodeType = ob.toString(); + } + + retArr.add(" AAINodeType/VtxID for this Node = [" + nodeType + "/" + v.id() + "]"); + retArr.add(" Property Detail: "); + Iterator<VertexProperty<Object>> pI = v.properties(); + while( pI.hasNext() ){ + VertexProperty<Object> tp = pI.next(); + Object val = tp.value(); + retArr.add("Prop: [" + tp.key() + "], val = [" + val + "] "); + } + } + return retArr; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbEdgeGroup.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbEdgeGroup.java new file mode 100644 index 0000000..56a9a4b --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbEdgeGroup.java @@ -0,0 +1,439 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphgen; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.collections.map.MultiValueMap; +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.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.dbgen.DbMeth; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.exceptions.AAIExceptionWithInfo; +import org.openecomp.aai.ingestModel.DbMaps; +import org.openecomp.aai.ingestModel.IngestModelMoxyOxm; +import org.openecomp.aai.serialization.db.EdgeRule; +import org.openecomp.aai.serialization.db.EdgeRules; +import org.openecomp.aai.serialization.db.EdgeType; +import org.openecomp.aai.serialization.db.MultiplicityRule; +import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.thinkaurelius.titan.core.TitanEdge; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + +public class DbEdgeGroup { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DbEdgeGroup.class); + + /** + * Replace edge group. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param scope the scope + * @param relatedNodesMultiMap the related nodes multi map + * @param apiVersion the api version + * @throws AAIException the AAI exception + */ + public static void replaceEdgeGroup( String transId, + String fromAppId, + TitanTransaction graph, + TitanVertex startVert, + String scope, + MultiValueMap relatedNodesMultiMap, // supports multiple entries for same nodetype + String apiVersion ) throws AAIException{ + + // -------------------------------------------------------------------- + // NOTE --- This routine is only used for "cousin" relationships. + // -------------------------------------------------------------------- + + /* + * scope can be one of these: + * a) "ALL_COUSIN_REL" + * b) "ONLY_PASSED_COUSINS_REL" (only look at the edge types that are + * represented in the passed list of relationships) + * + * Given a startNode and a list of relationshipInfo, we need to check the database and then, + * 1) Delete any in-scope db relationships which are not represented in the relationshipList. + * So, for ALL_COUSIN_REL, we would delete any db relationship that had an edge with + * parentOf = false if it was not represented in the passed Relationship List. + * For ONLY_PASSED_COUSINS_REL - we'd do the same as above, but only for edges that match the + * type in the passed relationshipList. We'd leave any others alone. + * 2) Then, Persist (add/update) all the remaining passed-in relationships. + */ + + if( !scope.equals("ALL_COUSIN_REL") && !scope.equals("ONLY_PASSED_COUSINS_REL") ){ + throw new AAIException("AAI_6120", "Illegal scope parameter passed: [" + scope + "]."); + } + + HashMap <String,String> vidToNodeTypeInDbHash = new HashMap <String,String>(); + HashMap <String,String> nodeTypeInReqHash = new HashMap <String,String>(); + HashMap <String,TitanEdge> vidToEdgeInDbHash = new HashMap <String,TitanEdge>(); + HashMap <String,TitanVertex> vidToTargetVertexHash = new HashMap <String,TitanVertex>(); + + //------------------------------------------------------------------------------------------------------------ + // 1) First -- look what is currently in the db -- + // "cousins" => grab all nodes connected to startVertex that have edges with param: isParent = false. + //------------------------------------------------------------------------------------------------------------ + GraphTraversalSource conPipeTraversal = startVert.graph().traversal(); + GraphTraversal<Vertex, Edge> conPipe = conPipeTraversal.V(startVert).bothE().has("isParent",false); + // Note - it's ok if nothing is found + if( conPipe != null ){ + while( conPipe.hasNext() ){ + TitanEdge ed = (TitanEdge) conPipe.next(); + TitanVertex cousinV = ed.otherVertex(startVert); + String vid = cousinV.id().toString(); + String noTy = cousinV.<String>property("aai-node-type").orElse(null); + vidToNodeTypeInDbHash.put(vid, noTy); + vidToEdgeInDbHash.put(vid, ed); + + LOGGER.info("Found connected cousin vid(s) in db: [" + cousinV.id().toString() + "]"); + } + } + + //------------------------------------------------------------------------------------------------------------ + //2) Get a List of the Titan nodes that the end-state list wants to be connected to + //------------------------------------------------------------------------------------------------------------ + ArrayList <TitanVertex> targetConnectedToVertList = new ArrayList<TitanVertex>(); + if( relatedNodesMultiMap != null ) { + + Set entrySet = relatedNodesMultiMap.entrySet(); + Iterator it = entrySet.iterator(); + //System.out.println(" Object key Object value"); + while (it.hasNext()) { + Map.Entry mapEntry = (Map.Entry) it.next(); + String rel2Nt = (String) mapEntry.getKey(); + int i = 0; + ArrayList <HashMap<String, Object>> propList = ((ArrayList<HashMap<String, Object>>)relatedNodesMultiMap.get(rel2Nt)); + while (i < propList.size()) { + HashMap<String, Object> propFilterHash = (HashMap<String, Object>) propList.get(i++); + + TitanVertex targetVert; + + try { + targetVert = DbMeth.getUniqueNodeWithDepParams( transId, + fromAppId, + graph, + rel2Nt, + propFilterHash, + apiVersion ); + } catch (AAIException e) { + if (e.getErrorObject().getErrorCode().equals("6114")) + throw new AAIExceptionWithInfo("AAI_6129", + e, + "Node of type " + rel2Nt + " not found for properties:" + propFilterHash.toString(), + propFilterHash, + rel2Nt); + else + throw e; + } + + targetConnectedToVertList.add(targetVert); + + String vid = targetVert.id().toString(); + String noTy = targetVert.<String>property("aai-node-type").orElse(null); + nodeTypeInReqHash.put(noTy, ""); + vidToTargetVertexHash.put(vid, targetVert); + + LOGGER.info("They request edges to these vids:[" + targetVert.id().toString() + "]"); + } + } + } + + //------------------------------------------------------------------------------------------------------------------- + // 3) Compare what is in the DB with what they are requesting as an end-state. + // If any are found in the db-list but not the new-list, delete them from the db (conditionally - based on scope) + //------------------------------------------------------------------------------------------------------------------- + String startVtxNT = startVert.<String>property("aai-node-type").orElse(null); + for( Map.Entry<String, TitanEdge> entry : vidToEdgeInDbHash.entrySet() ){ + String vertId = entry.getKey(); + TitanEdge dbEd = entry.getValue(); + if( ! vidToTargetVertexHash.containsKey(vertId) ){ + if( scope.equals("ALL_COUSIN_REL") ){ + LOGGER.info("We will DELETE existing DB-edge to vids = [" + entry.getKey() + "]"); + DbMeth.removeAaiEdge(transId, fromAppId, graph, dbEd); + } + else if( scope.equals("ONLY_PASSED_COUSINS_REL") ){ + // If they use "ONLY_PASSED_COUSINS_REL" scope, they want us to delete an edge ONLY if: + // a) this edge is the same type that they passed in (but goes to a different endpoint) + // AND b) this additional edge would break the multiplicity edge rule. + String ntInDb = vidToNodeTypeInDbHash.get(vertId); + if( nodeTypeInReqHash.containsKey(ntInDb) && additionalEdgeWouldBreakMultEdgeRule(startVtxNT, ntInDb) ){ + LOGGER.info("We will DELETE existing DB-edge to vids = [" + entry.getKey() + "]"); + DbMeth.removeAaiEdge(transId, fromAppId, graph, dbEd); + } + } + } + } + + //--------------------------------------------------------------- + // 4) add/update (persist) all the relations on the new-list + //--------------------------------------------------------------- + for( Map.Entry<String, TitanVertex> entry : vidToTargetVertexHash.entrySet() ){ + LOGGER.info("Call persistAaiEdge on edge(s) going to vids = " + "[" + entry.getKey() + "]"); + TitanVertex targV = entry.getValue(); + DbMeth.persistAaiEdge(transId, fromAppId, graph, startVert, targV, apiVersion, "cousin"); + } + + return; + +}// End replaceEdgeGroup() + + +/** + * Additional edge would break mult edge rule. + * + * @param startNodeType the start node type + * @param endNodeType the end node type + * @return the boolean + * @throws AAIException the AAI exception + */ +private static Boolean additionalEdgeWouldBreakMultEdgeRule( String startNodeType, String endNodeType ) + throws AAIException { + // Return true if a second edge from the startNodeType to the endNodeType would + // break a multiplicity edge rule. + // Ie. Adding an edge to a second tenant (endNode) from a vserver (startNode) node would be flagged by this + // if we have our multiplicity rule set up for the "vserver-tenant" edge set up as "Many2One" or if + // it was set up the other way, "tenant-vserver" as "One2Many" or even if it was "One2One". In any of + // those scenarios, the addition of an edge from a particular vserver to an additional tenant node + // would break the rule. + + EdgeRule edgeRule = null; + boolean reversed = false; + + if (EdgeRules.getInstance().hasEdgeRule(startNodeType, endNodeType)) { + } else if (EdgeRules.getInstance().hasEdgeRule(endNodeType, startNodeType)) { + reversed = true; + } + + try { + edgeRule = EdgeRules.getInstance().getEdgeRule(EdgeType.COUSIN, startNodeType, endNodeType); + } catch (NoEdgeRuleFoundException e) { + return false; + } + + + if (edgeRule.getMultiplicityRule().equals(MultiplicityRule.ONE2ONE)) { + return true; + } else if (reversed && edgeRule.getMultiplicityRule().equals(MultiplicityRule.ONE2MANY)) { + return true; + } else if (!reversed && edgeRule.getMultiplicityRule().equals(MultiplicityRule.MANY2ONE)) { + return true; + } else { + return false; + } + + +}// end of additionalEdgeWouldBreakMultEdgeRule() + +/** + * Delete edge group. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param relatedNodesMultiMap the related nodes multi map + * @param apiVersion the api version + * @return void + * @throws AAIException the AAI exception + */ +public static void deleteEdgeGroup( String transId, + String fromAppId, + TitanTransaction graph, + TitanVertex startVert, + MultiValueMap relatedNodesMultiMap, + String apiVersion ) throws AAIException{ + // -------------------------------------------------------------------- + // NOTE - This routine is only used for "cousin" relationships. + // ALSO - an edge deletion will fail if that edge was needed by + // the node on one of the sides for uniqueness. We're just + // being careful here - so far, I don't think a cousin-edge + // is ever used for this kind of dependency. + // -------------------------------------------------------------------- + + HashMap <String,TitanEdge> cousinVidToEdgeInDbHash = new HashMap <String,TitanEdge>(); + String startVertNT = startVert.<String>property("aai-node-type").orElse(null); + String startVertVid = startVert.id().toString(); + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(apiVersion); + Collection <String> startVertDepNTColl = dbMaps.NodeDependencies.get(startVertNT); + + //----------------------------------------------------------------------------------------------------- + // Get a list of vertexes that are attached to the startVert as "cousins" and the connecting edges + //----------------------------------------------------------------------------------------------------- + GraphTraversalSource conPipeTraversal = startVert.graph().traversal(); + GraphTraversal<Vertex, Edge> conPipe = conPipeTraversal.V(startVert).bothE().has("isParent",false); + if( conPipe != null ){ + while( conPipe.hasNext() ){ + TitanEdge ed = (TitanEdge) conPipe.next(); + TitanVertex cousinV = ed.otherVertex(startVert); + String vid = cousinV.id().toString(); + cousinVidToEdgeInDbHash.put(vid, ed); + } + } + + //------------------------------------------------------------- + // Look through the Relationship info passed in. + // Delete edges as requested if they check-out as cousins. + //------------------------------------------------------------- + Boolean isFirst = true; + String msg = "Deleting edges from vid = " + startVertVid + "(" + startVertNT + "), to these: ["; + if( relatedNodesMultiMap != null ) { + Set entrySet = relatedNodesMultiMap.entrySet(); + Iterator it = entrySet.iterator(); + //System.out.println(" Object key Object value"); + while (it.hasNext()) { + Map.Entry mapEntry = (Map.Entry) it.next(); + String rel2Nt = (String) mapEntry.getKey(); + HashMap<String, Object> propFilterHash = (HashMap<String, Object>)((ArrayList) relatedNodesMultiMap.get(rel2Nt)).get(0); + TitanVertex otherEndVert = DbMeth.getUniqueNodeWithDepParams( transId, fromAppId, graph, rel2Nt, propFilterHash, apiVersion ); + String otherNT = otherEndVert.<String>property("aai-node-type").orElse(null); + String reqDelConnectedVid = otherEndVert.id().toString(); + if( !cousinVidToEdgeInDbHash.containsKey(reqDelConnectedVid) ){ + throw new AAIException("AAI_6127", "COUSIN Edge between " + startVertVid + " (" + startVertNT + ") and " + reqDelConnectedVid + + "(" + otherNT + ") not found. "); + } + else { + // This was a cousin edge. But before we delete it, we will check to make + // sure it doesn't have a unique-dependency issue (need to check in two directions) + Iterator <String> ntItr1 = startVertDepNTColl.iterator(); + if( ntItr1.hasNext() ){ + while( ntItr1.hasNext() ){ + if( ntItr1.next().equals(otherNT) ){ + throw new AAIException("AAI_6126", "Edge between " + startVertVid + " and " + reqDelConnectedVid + + " cannot be deleted because of a uniqueness-dependancy between nodeTypes, " + + startVertNT + " and " + otherNT); + } + } + } + + Collection <String> depNTColl = dbMaps.NodeDependencies.get(otherNT); + Iterator <String> ntItr2 = depNTColl.iterator(); + if( ntItr2.hasNext() ){ + while( ntItr2.hasNext() ){ + if( ntItr2.next().equals(startVertNT) ){ + throw new AAIException("AAI_6126", "Edge between " + startVertVid + " and " + reqDelConnectedVid + + " cannot be deleted because of a uniqueness-dependancy between nodeTypes: " + + otherNT + " and " + startVertNT); + } + } + } + + // It's OK to delete this edge as requested. + if( ! isFirst ){ + msg = msg + ", "; + } + isFirst = false; + msg = msg + reqDelConnectedVid + "(" + otherNT + ")"; + TitanEdge targetDelEdge = cousinVidToEdgeInDbHash.get(reqDelConnectedVid); + DbMeth.removeAaiEdge(transId, fromAppId, graph, targetDelEdge); + } + } + } + + msg = msg + "]"; + + LOGGER.info(msg); + + return; + +}// End deleteEdgeGroup() + + +/** + * Gets the edge group. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param vidToNodeTypeHash the vid to node type hash + * @param vidToVertexHash the vid to vertex hash + * @param scope the scope + * @param apiVersion the api version + * @return void + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ +public static void getEdgeGroup( String transId, + String fromAppId, + TitanTransaction graph, + TitanVertex startVert, + HashMap <String,String> vidToNodeTypeHash, + HashMap <String,TitanVertex> vidToVertexHash, + String scope, + String apiVersion ) throws AAIException, UnsupportedEncodingException{ + + LOGGER.debug("scope={}", scope); + LOGGER.debug("vid={}", startVert.id().toString()); + LOGGER.debug("nodetype={}", startVert.property("aai-node-type").orElse(null).toString()); + + /* + * scope can be one of these: + * 1) "ONLY_COUSIN_REL" <-- This is the only one supported for now + * 2) "ALL_COUSIN_AND_CHILDREN_REL" + * 3) "ONLY_CHILDREN" + * 4) "USES_RESOURCE" + * + * Given a startNode and the scope, we need to return relationships that we find in the DB + */ + + if( !scope.equals("ONLY_COUSIN_REL") ){ + throw new AAIException("AAI_6120", "Illegal scope parameter passed: [" + scope + "]."); + } + + //------------------------------------------------------------------------------------------------------------ + // Grab "first-layer" vertexes from the in the db -- + // "cousins" => grab all nodes connected to startVertex that have edges with param: isParent = false. + // "children" => grab nodes via out-edge with isParent = true (NOT YET SUPPORTED) + //------------------------------------------------------------------------------------------------------------ + Iterable<Vertex> qResult = startVert.query().has("isParent",false).vertices(); + Iterator <Vertex> resultI = qResult.iterator(); + + while( resultI.hasNext() ){ + TitanVertex cousinV = (TitanVertex)resultI.next(); + //showPropertiesForNode( transId, fromAppId, cousinV ); + + String vid = cousinV.id().toString(); + String noTy = cousinV.<String>property("aai-node-type").orElse(null); + vidToNodeTypeHash.put(vid, noTy); + vidToVertexHash.put(vid, cousinV); + + LOGGER.debug("Found connected cousin vid(s) in db: " + "[" + cousinV.id().toString() + "]"); + } + +}// End getEdgeGroup() + + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbMeth.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbMeth.java new file mode 100644 index 0000000..5951a10 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/DbMeth.java @@ -0,0 +1,3643 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphgen; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.apache.tinkerpop.gremlin.structure.VertexProperty; + +import org.openecomp.aai.dbmodel.DbEdgeRules; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.ingestModel.DbMaps; +import org.openecomp.aai.ingestModel.IngestModelMoxyOxm; +import org.openecomp.aai.serialization.db.EdgeRule; +import org.openecomp.aai.serialization.db.EdgeRules; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.net.InetAddresses; +import com.thinkaurelius.titan.core.TitanEdge; +import com.thinkaurelius.titan.core.TitanGraph; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + + +/** + * General Database-level Utility class. These methods deal with the database one dataNode / Edge at a time. + * Transactions are managed at a higher level by the calling classes by passing in a TitanTransaction object. + */ +public class DbMeth{ + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DbMeth.class); + + /** + * Patch aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param depNodeVal the dep node val + * @param apiVersion the api version + * @return TitanVertex + * @throws AAIException the AAI exception + */ + public static TitanVertex patchAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, TitanVertex depNodeVal, String apiVersion ) throws AAIException{ + // If they're calling patchAaiNode, then we only want to add/update the properties that they + // pass us in the propHash. If there are others already in the DB, we leave them alone. + + // Note: to be really official, we'd throw an error if the node wasn't already in the db. + boolean[] objectExists = new boolean[1]; + objectExists[0] = true; + Boolean patchOnly = true; + TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists); + return( tv ); + + } // end of patchAaiNode() + + /** + * Patch aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param depNodeVal the dep node val + * @return the titan vertex + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex patchAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, TitanVertex depNodeVal) throws AAIException{ + return patchAaiNode( transId, fromAppId, graph, nodeType, + propHash, depNodeVal, null ); + } + + /** + * Persist aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param depNodeVal the dep node val + * @param patchOnly the patch only + * @param apiVersion the api version + * @return the titan vertex + * @throws AAIException the AAI exception + */ + public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, String apiVersion) throws AAIException{ + boolean[] objectExists = new boolean[1]; + objectExists[0] = false; + return persistAaiNodeBASE( transId, fromAppId, graph, nodeType, + propHash, depNodeVal, patchOnly, apiVersion, objectExists); + } + + /** + * Persist aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param addIfNotFound the add if not found + * @param depNodeVal the dep node val + * @param apiVersion the api version + * @return the titan vertex + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion) throws AAIException{ + // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us. That is, if + // there is already a record in the DB, but they do not pass some of the existing properties, they should + // be cleared from the DB. Since we want to take care of all properties, we pass patchOnly = false + Boolean patchOnly = false; + boolean[] objectExists = new boolean[1]; + objectExists[0] = false; + TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists); + return( tv ); + } + + /** + * Persist aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param addIfNotFound the add if not found + * @param depNodeVal the dep node val + * @return the titan vertex + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal) throws AAIException{ + // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us. That is, if + // there is already a record in the DB, but they do not pass some of the existing properties, they should + // be cleared from the DB. Since we want to take care of all properties, we pass patchOnly = false + Boolean patchOnly = false; + boolean[] objectExists = new boolean[1]; + objectExists[0] = false; + TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, null, objectExists); + return( tv ); + } // end of persistAaiNode() + + /** + * Persist aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param addIfNotFound the add if not found + * @param depNodeVal the dep node val + * @param apiVersion the api version + * @param objectExists the object exists + * @return TitanVertex + * @throws AAIException the AAI exception + */ + public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion, boolean[] objectExists) throws AAIException{ + Boolean patchOnly = false; + // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us. That is, if + // there is already a record in the DB, but they do not pass some of the existing properties, they should + // be cleared from the DB. Since we want to take care of all properties, we pass patchOnly = false + TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, null); + return( tv ); + } + + /** + * Persist aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param addIfNotFound the add if not found + * @param depNodeVal the dep node val + * @param apiVersion the api version + * @param objectExists the object exists + * @param thisNodeVertex the this node vertex + * @return the titan vertex + * @throws AAIException the AAI exception + */ + public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion, boolean[] objectExists, TitanVertex thisNodeVertex) throws AAIException{ + Boolean patchOnly = false; + // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us. That is, if + // there is already a record in the DB, but they do not pass some of the existing properties, they should + // be cleared from the DB. Since we want to take care of all properties, we pass patchOnly = false + TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, thisNodeVertex); + return( tv ); + } + + /** + * Persist aai node BASE. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param depNodeVal the dep node val + * @param patchOnly the patch only + * @param apiVersion the api version + * @param objectExists the object exists + * @return the titan vertex + * @throws AAIException the AAI exception + */ + public static TitanVertex persistAaiNodeBASE(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, + String apiVersion, boolean[] objectExists) throws AAIException{ + return persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, null); + } + + /** + * Persist aai node BASE. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propHash the prop hash + * @param depNodeVal the dep node val + * @param patchOnly the patch only + * @param apiVersion the api version + * @param objectExists the object exists + * @param thisNodeVertex the this node vertex + * @return the titan vertex + * @throws AAIException the AAI exception + */ + public static TitanVertex persistAaiNodeBASE(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, + String apiVersion, boolean[] objectExists, TitanVertex thisNodeVertex) throws AAIException{ + + if( graph == null ){ + throw new AAIException("AAI_6101", "null graph object passed to persistAaiNodeBASE()"); + } + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + boolean useDepNode = false; + String resourceVersion = null; + if( propHash.containsKey("resource-version") ){ + resourceVersion = (String)(propHash.get("resource-version")); + } + String aaiUniqueKeyVal = null; + if( propHash.containsKey("aai-unique-key") ){ + // Note -- we are assuming that nobody is monkeying with this. The 16-07 first-pass theory + // is that the REST layer is always gonna generate this or pass it through. + aaiUniqueKeyVal = (String)(propHash.get("aai-unique-key")); + propHash.remove("aai-unique-key"); + } + + if( needsADepNode4Uniqueness(transId, fromAppId, nodeType, apiVersion) ){ + // This kind of node needs a dependent node (for uniqueness) + if( depNodeVal == null ){ + // They should have passed in the node that this one depends on + throw new AAIException("AAI_6109", "null dependentNode object passed to persistAaiNodeBASE() but " + nodeType + " requires one."); + } + else if( ! nodeTypeACanDependOnB(transId, fromAppId, nodeType, depNodeVal.<String>property("aai-node-type").orElse(null), apiVersion) ){ + // They should have passed in the right type of node as the dependent node + throw new AAIException("AAI_6109", "dependentNode of type " + depNodeVal.<String>property("aai-node-type").orElse(null) + " passed to persistAaiNodeBASE() for nodeType" + nodeType + "."); + } + useDepNode = true; + } + else { + depNodeVal = null; + } + + // Note: as of 1607, we no longer validate property names since that's covered by the REST layer. + // Same goes for required fields (as of 1602) + + // Special ip-address validation for ipAddr nodes only... This will go away when we go to YANG and + // do validations like this up at that layer. + if( nodeType.equals("ipaddress") ){ + // Note - this will throw an exception if the ipAddress is using a bad format + ipAddressFormatOK( transId, fromAppId, (String)propHash.get("addr"), (String)propHash.get("version") ); + } + + // Use the key-fields/dependentNode to check if this is an add or an update + // We assume that all NodeTypes at least one key-property defined. A dependentNode is optional. + if( ! dbMaps.NodeKeyProps.containsKey(nodeType) ){ + // Problem if no key Properties defined for this nodeType + String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + throw new AAIException("AAI_6105", "No node-key-properties defined in dbMaps for nodeType = " + nodeType + " (ver=" + defVer + ")"); + } + + Boolean hasAltKey1 = false; + HashMap <String,Object>nodeAltKey1PropsHash = new HashMap<String,Object>(); + Collection <String> altKey1Props = getNodeAltKey1PropNames(transId, fromAppId, nodeType, apiVersion); + if( altKey1Props != null ){ + Iterator <String> altKey1PropI = altKey1Props.iterator(); + while( altKey1PropI.hasNext() ){ + String propName = altKey1PropI.next(); + // NOTE: alt-keys are not always required fields. If it is null or blank, we won't + // do alt-key checks on it. + Object value = propHash.get(propName); + if( value != null && !value.toString().equals("") ){ + hasAltKey1 = true; + nodeAltKey1PropsHash.put(propName, value); + } + } + } + HashMap <String,Object>nodeKeyPropsHash = new HashMap<String,Object>(); + Collection <String> keyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion); + Iterator <String> keyPropI = keyProps.iterator(); + while( keyPropI.hasNext() ){ + String propName = keyPropI.next(); + + Object value = propHash.get(propName); + nodeKeyPropsHash.put(propName, value); + } + + // Check if this node is already in the database based on the Primary Key Info + TitanVertex existingVert = thisNodeVertex; + boolean foundTheNodeInDb = true; + + if (existingVert == null) { + try { + existingVert = getUniqueNode( transId, fromAppId, graph, nodeType, nodeKeyPropsHash, depNodeVal, apiVersion ); + } + catch (AAIException e) { + if (e.getErrorObject().getErrorCode().equals("6114")) { + foundTheNodeInDb = false; + } + else { + throw e; + } + } + } + + // this is so the notification knows whether or not the operation was an UPDATE or a CREATe + objectExists[0] = foundTheNodeInDb; + if( foundTheNodeInDb ){ + // A record was found in the DB using the PK. + if( needToDoResourceVerCheck(apiVersion, patchOnly) ){ + // Need to check that they knew what they were updating + String existingResVer = existingVert.<String>property("resource-version").orElse(null); + if( resourceVersion == null || resourceVersion.equals("") ){ + throw new AAIException("AAI_6130", "Resource-version not passed for update of = " + nodeType + ", " + nodeKeyPropsHash.toString()); + } + else if( (existingResVer != null) && !resourceVersion.equals(existingResVer) ){ + throw new AAIException("AAI_6131", "Resource-version " + resourceVersion + " MISMATCH WITH EXISTING " + existingResVer + " for update of = " + nodeType + ", " + nodeKeyPropsHash.toString()); + } + } + + // Need to ensure that the Alternate key isn't changing to a value that points to a different existing node. + // It is ok if it points to nothing -- that would just be an update for this node. It's also ok if + // it points to this (existing) node - that just means that it wasn't being updated. + if( hasAltKey1 ){ + try { + TitanVertex chkVert = getUniqueNode( transId, fromAppId, graph, nodeType, nodeAltKey1PropsHash, depNodeVal, apiVersion ); + if( ! chkVert.id().toString().equals(existingVert.id().toString()) ){ + throw new AAIException("AAI_6117", "In-Use AlternateKey value passed for update of nodeType = " + nodeType); + } + } + catch (AAIException e) { + if(! e.getErrorObject().getErrorCode().equals("6114") ){ + throw e; + } + } + } + } + else { + // Note not in the DB -- This will be an ADD of a new node + // a) make sure they didn't say they were just doing "patchOnly" which cannot be an ADD. + // b) if there is an alternate key, we need to make sure the AK isn't already in use by somebody else. + if( patchOnly ){ + String depMsg = ""; + if( useDepNode ){ + depMsg = " plus dependent node. "; + } + throw new AAIException("AAI_6114", "Patch Request, but no Node of type " + nodeType + " found for properties: [" + propHash + "] " + depMsg); + } + + if( needToDoResourceVerCheck(apiVersion, patchOnly) && (resourceVersion != null) && !resourceVersion.equals("") ){ + throw new AAIException("AAI_6131", "Resource-version was passed in, but this is an ADD of a " + nodeType + ", with these params: " + nodeKeyPropsHash.toString()); + } + if( hasAltKey1 ){ + try { + getUniqueNode( transId, fromAppId, graph, nodeType, nodeAltKey1PropsHash, depNodeVal, apiVersion ); + // Since the Primary Key for this nodeType wasn't found in the DB yet, the fact that + // we are able to find a record (no "6114" exception thrown) using the Alternate-Key is an error. + // We can't create a new node that uses an AK that's already in use. + throw new AAIException("AAI_6117", "Conflicting Key and Alternate-Key values passed for add of nodeType = " + nodeType); + } + catch (AAIException e) { + if(! e.getErrorObject().getErrorCode().equals("6114") ){ + throw e; + } + } + } + } + + // ------------- Done with checking. Do the add or update to the dB ----------------------- + + if( foundTheNodeInDb ){ + long unixTimeNow = System.currentTimeMillis() / 1000L; + // ----- This is an UPDATE ------ + + + String existingSourceOfTruth = fromAppId; // default value if we can't get the old one + Object tmpOb = existingVert.<Object>property("source-of-truth").orElse(null); + if( tmpOb != null ){ + existingSourceOfTruth = tmpOb.toString(); + } + long existingCreateTs = unixTimeNow; // default value if we can't get the old one + tmpOb = existingVert.<Object>property("aai-created-ts").orElse(null); + if( tmpOb != null ){ + existingCreateTs = (long) tmpOb; + } + + String msg = "UPDATE vertex of type = [" + nodeType + "] "; + if( useDepNode ){ + String depNType = depNodeVal.<String>property("aai-node-type").orElse(null); + HashMap <String, Object> depNodePropKeysHash = getNodeKeyPropHash(transId, fromAppId, graph, depNodeVal); + LOGGER.info("UPDATE existing node: type = " + nodeType + ", key(s) = [" + nodeKeyPropsHash + + "] which rides on dependent node: type = " + depNType + ", with key(s) = [" + depNodePropKeysHash + "]."); + } + else { + LOGGER.info("UPDATE existing node: type = " + nodeType + ", key(s) = [" + nodeKeyPropsHash + "] (no dep. node)."); + } + String removeList = ""; + if( ! patchOnly ){ + // They are updating an existing record, and they want us to "process all defined properties" (not just patch) + // So we will see if the node has any properties that were not passed-in. Those need to be removed. + Collection <String> propCol = dbMaps.NodeProps.get(nodeType); + Iterator <String> propIter = propCol.iterator(); + while( propIter.hasNext() ){ + String propName = propIter.next(); + if( ! propHash.containsKey(propName) && !DbEdgeRules.ReservedPropNames.containsKey(propName)){ + if( thisPropertyWasPutByNewerVersionOfCode(apiVersion, nodeType, propName) ){ + // we must be using an older version of code here - but the property that + // has not been passed in this persist call is one that this older version of + // the database did not know about. So leave it alone. + } + else { + removeList = removeList + "," + propName; + existingVert.property(propName).remove(); + } + } + } + } + if( !removeList.equals("") ){ + LOGGER.info("Removed these props on update: [" + removeList + "]"); + } + for( Map.Entry<String, Object> entry : propHash.entrySet() ){ + // update the parameters that have been passed in (except the key-properties) + // taking away the key-property check. We will now allow this since + // the keys were used to identify this node, so they should be good and + // there are times when Titan resolves conflicts by only using the + // data set in an update - and was losing our key info... + // Similar to the change noted below. + //if( ! nodeKeyPropsHash.containsKey(entry.getKey()) ){ + // existingVert.setProperty( entry.getKey(), entry.getValue() ); + //} + if( ! entry.getKey().equals("resource-version") ){ + boolean nonSingleCardinality = false; + boolean setSoNoDupes = false; + if( checkPropCardinality(entry.getKey(), "Set") ){ + nonSingleCardinality = true; + setSoNoDupes = true; + } + else if( checkPropCardinality(entry.getKey(), "List") ){ + nonSingleCardinality = true; + } + + Iterator <Object> valIter = null; + if( nonSingleCardinality ){ + String className = entry.getValue().getClass().getSimpleName(); + if( className.equals("ArrayList") ){ + valIter = ((ArrayList)(entry.getValue())).iterator(); + } + else if( className.equals("List") ){ + valIter = ((List)(entry.getValue())).iterator(); + } + else if( className.equals("Set") ){ + valIter = ((Set)(entry.getValue())).iterator(); + } + } + + if( nonSingleCardinality ){ + // This property has Cardinality of List or Set - which need to be handled carefully + // Note -- for Lists or Sets, we assume they are of dataType String - that is all + // the Rest layer supports at the moment (16-02) + ArrayList <String> currentData = new ArrayList <String> (); + if( patchOnly ){ + // When patching - gotta know what's already in the db + Iterator<VertexProperty<Object>> existingPropsIter = (existingVert.properties(entry.getKey())); + if( existingPropsIter != null ){ + while( existingPropsIter.hasNext() ){ + String existingVal = existingPropsIter.next().value().toString(); + currentData.add( existingVal ); + } + } + } + else { + // Since this is not a patch-update, we first have to clear out what is currently in the db. + existingVert.property(entry.getKey()).remove(); + } + + if( valIter != null ){ + while( valIter.hasNext() ){ + Object thisVal = valIter.next(); + if( setSoNoDupes ){ + // For Sets, we need to check that the data isn't already in the db or wasn't passed + // in to us twice in the propHash. Otherwise Titan throws an exception (instead of just ignoring it...) + if( !currentData.contains(thisVal) ){ + // We don't have this data yet, so add it to the Set + existingVert.property( entry.getKey(), thisVal ); + currentData.add( thisVal.toString() ); + } + } + else { + // For List data types, it's ok to have duplicate values in the db (why would we want this?) + existingVert.property( entry.getKey(), thisVal ); + } + } + } + } + else { + // This is a normal, "Cardinality = SINGLE" kind of property + // ResourceVersion is not populated based on passed-in data, it is set along with other internal properties below. + //Object cleanVal = convertTypeIfNeeded( entry.getKey(), entry.getValue() ); + //existingVert.setProperty( entry.getKey(), cleanVal ); + // ******************************** + existingVert.property( entry.getKey(), entry.getValue() ); + } + } + } + + // DEBUG - trying to deal with the case where simultaneous PUTs + // cause our db to wind up with a vertex that does not have these three properties filled in. + existingVert.property( "aai-node-type", nodeType ); + existingVert.property( "aai-created-ts", existingCreateTs ); + existingVert.property( "source-of-truth", existingSourceOfTruth ); + + if( aaiUniqueKeyVal != null ){ + existingVert.property( "aai-unique-key", aaiUniqueKeyVal ); + } + + existingVert.property( "aai-last-mod-ts", unixTimeNow ); + String resVers = "" + unixTimeNow; + existingVert.property( "resource-version", resVers ); + existingVert.property( "last-mod-source-of-truth", fromAppId ); + + LOGGER.info(msg + ", [aai-last-mod-ts]/[" + unixTimeNow + "]"); + + return( existingVert ); + } + else{ + // ----- Not found in the DB, This must be an ADD ------ + if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + throw new AAIException("AAI_6120", "nodeTypeCategory " + nodeType + " cannot be used to ADD a node. Need to pass a valid nodeType"); + } + + TitanVertex tiVnew = graph.addVertex( nodeType ); + + String msg = "ADD vertex of type = [" + nodeType + "] "; + if( depNodeVal != null ){ + String depNType = depNodeVal.<String>property("aai-node-type").orElse(null); + HashMap <String, Object> depNodePropKeysHash = getNodeKeyPropHash(transId, fromAppId, graph, depNodeVal); + msg = msg + " onto dependent node: type = " + depNType + ", which has key(s) = [" + depNodePropKeysHash + + "]. New Node Prop/values = "; + } + else { + msg = msg + " Note: no dependent node. New Node Prop/values = "; + } + boolean first = true; + for( Map.Entry<String, Object> entry : propHash.entrySet() ){ + if( ! entry.getKey().equals("resource-version") ){ + if( first ){ + msg = msg + " [" + entry.getKey() + "]/[" + entry.getValue() + "]"; + first = false; + } + else { + msg = msg + ", [" + entry.getKey() + "]/[" + entry.getValue() + "]"; + } + + boolean nonSingleCardinality = false; + boolean setSoNoDupes = false; + if( checkPropCardinality(entry.getKey(), "Set") ){ + nonSingleCardinality = true; + setSoNoDupes = true; + } + else if( checkPropCardinality(entry.getKey(), "List") ){ + nonSingleCardinality = true; + } + + Iterator <Object> valIter = null; + if( nonSingleCardinality ){ + String className = entry.getValue().getClass().getSimpleName(); + if( className.equals("ArrayList") ){ + valIter = ((ArrayList)(entry.getValue())).iterator(); + } + else if( className.equals("List") ){ + valIter = ((List)(entry.getValue())).iterator(); + } + else if( className.equals("Set") ){ + valIter = ((Set)(entry.getValue())).iterator(); + } + } + + if( nonSingleCardinality ){ + // This property has Cardinality of List or Set - which need to be handled carefully + ArrayList <String> currentData = new ArrayList <String> (); + if( valIter != null ){ + while( valIter.hasNext() ){ + Object thisVal = valIter.next(); + if( setSoNoDupes ){ + // For Sets, we need to check that they're not passing us duplicate data in propHash. + // Otherwise Titan throws an exception (instead of just ignoring it...) + if( !currentData.contains(thisVal) ){ + // We don't have this data yet, so add it to the Set + tiVnew.property( entry.getKey(), thisVal ); + currentData.add( thisVal.toString() ); + } + } + else { + // For List data types, it's ok to have duplicate values in the db (why would we want this?) + tiVnew.property( entry.getKey(), thisVal ); + } + } + } + } + else { + // This is a normal, "Cardinality = SINGLE" kind of property + // ResourceVersion is not populated based on passed-in data, it is set along with other internal properties below. + tiVnew.property( entry.getKey(), entry.getValue() ); + } + } + } + + tiVnew.property( "aai-node-type", nodeType ); + //long unixTime = System.currentTimeMillis() / 1000L; + long unixTime = System.currentTimeMillis(); + tiVnew.property( "aai-created-ts", unixTime ); + tiVnew.property( "aai-last-mod-ts", unixTime ); + String resVers = "" + unixTime; + tiVnew.property( "resource-version", resVers ); + tiVnew.property( "source-of-truth", fromAppId ); + tiVnew.property( "last-mod-source-of-truth", fromAppId ); + if( aaiUniqueKeyVal != null ){ + tiVnew.property( "aai-unique-key", aaiUniqueKeyVal ); + } + + LOGGER.info(msg + ", [aai-created-ts]/[" + unixTime + "]"); + return( tiVnew ); + } + + } // end of persistAaiNodeBASE() + + + /** + * Need to do resource ver check. + * + * @param apiVersion the api version + * @param patchOnlyFlag the patch only flag + * @return the boolean + * @throws AAIException the AAI exception + */ + public static Boolean needToDoResourceVerCheck(String apiVersion, Boolean patchOnlyFlag) + throws AAIException{ + + if( patchOnlyFlag ){ + // we do not do resource checking for patch requests. + return false; + } + + String resourceCheckOnFlag = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG); + + int apiVerInt = cleanUpApiVersion(apiVersion); + + if( (resourceCheckOnFlag != null) && resourceCheckOnFlag.equals("true") ){ + // Only do the check if the resource enable flag is set to "true" + if( apiVerInt > 4 ){ + // We're only doing the resource version checks for v5 and later + return true; + } + } + + return false; + }// End needToDoResourceVerCheck() + + + /** + * Clean up api version. + * + * @param apiVersionString the api version string + * @return the int + * @throws AAIException the AAI exception + */ + private static int cleanUpApiVersion( String apiVersionString ) throws AAIException { + // Note: we expect an apiVersion to start with the letter "v", followed by an integer. + + int versionInt = 0; + String verStr = apiVersionString; + if( (apiVersionString == null) || (apiVersionString.length() < 2) ){ + // Passed in version doesn't look right + verStr = org.openecomp.aai.util.AAIApiVersion.get(); + } + versionInt = getVerNumFromVerString( verStr ); + + return versionInt; + } + + /** + * Gets the ver num from ver string. + * + * @param versionString the version string + * @return the ver num from ver string + * @throws AAIException the AAI exception + */ + private static int getVerNumFromVerString( String versionString )throws AAIException { + int versionInt = 0; + if( versionString == null || versionString.length() < 2 ){ + throw new AAIException("AAI_6121", " Bad Version (format) passed to getVerNumFromVerString: [" + versionString + "]."); + } + + int strLen = versionString.length(); + // We assume that a version looks like "v" followed by an integer + if( ! versionString.substring(0,1).equals("v") ){ + String detail = " Bad Version (format) passed to getVerNumFromVerString: [" + versionString + "]."; + throw new AAIException("AAI_6121", detail); + } + else { + String intPart = versionString.substring(1,strLen); + try { + versionInt = Integer.parseInt( intPart ); + } + catch( Exception e ){ + String detail = " Bad Version passed to getVerNumFromVerString: [" + versionString + "]."; + throw new AAIException("AAI_6121", detail); + } + } + return versionInt; + } + + + /** + * Gets the node key prop names. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return HashMap of keyProperties + * @throws AAIException the AAI exception + */ + public static Collection <String> getNodeKeyPropNames( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + Collection <String> keyProps = new ArrayList <String>(); + if( dbMaps.NodeKeyProps.containsKey(nodeType) ){ + keyProps = dbMaps.NodeKeyProps.get(nodeType); + } + else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + // The passed-in nodeType was really a nodeCategory, so we need to look up the key params + Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType); + Iterator <String> catItr = nTypeCatCol.iterator(); + String catInfo = ""; + if( catItr.hasNext() ){ + // For now, we only look for one. + catInfo = catItr.next(); + } + else { + String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType+ " (ver=" + defVer + ")"); + } + + String [] flds = catInfo.split(","); + if( flds.length != 4 ){ + throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data for nodeType = [" + nodeType + "]."); + } + + String keyPropsString = flds[0]; + String [] propNames = keyPropsString.split("\\|"); + for( int i = 0; i < propNames.length; i++ ){ + keyProps.add(propNames[i]); + } + } + else { + String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType+ " (ver=" + defVer + ")"); + } + + return keyProps; + + }// end of getNodeKeyPropNames + + /** + * Gets the node key prop names. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @return the node key prop names + * @throws AAIException the AAI exception + */ + @Deprecated + public static Collection <String> getNodeKeyPropNames( String transId, String fromAppId, String nodeType ) throws AAIException{ + return getNodeKeyPropNames( transId, fromAppId, nodeType, null); + } + + /** + * Gets the node alt key 1 prop names. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return HashMap of keyProperties + * @throws AAIException the AAI exception + */ + public static Collection <String> getNodeAltKey1PropNames( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + Collection <String> altKey1Props = new ArrayList <String>(); + if( dbMaps.NodeAltKey1Props.containsKey(nodeType) ){ + altKey1Props = dbMaps.NodeAltKey1Props.get(nodeType); + } + else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + // The passed-in nodeType was really a nodeCategory, so we need to look up the key params + Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType); + Iterator <String> catItr = nTypeCatCol.iterator(); + String catInfo = ""; + if( catItr.hasNext() ){ + catInfo = catItr.next(); + String [] flds = catInfo.split(","); + if( flds.length != 4 ){ + throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); + } + + String altKeyPropsString = flds[1]; + String [] propNames = altKeyPropsString.split("\\|"); + for( int i = 0; i < propNames.length; i++ ){ + altKey1Props.add(propNames[i]); + } + } + } + + return altKey1Props; + + }// end of getNodeAltKey1PropNames + + /** + * Gets the node alt key 1 prop names. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @return the node alt key 1 prop names + * @throws AAIException the AAI exception + */ + @Deprecated + public static Collection <String> getNodeAltKey1PropNames( String transId, String fromAppId, String nodeType ) throws AAIException{ + return getNodeAltKey1PropNames( transId, fromAppId, nodeType, null); + } + + + /** + * Gets the unique node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param keyPropsHash the key props hash + * @param depNodeVal the dep node val + * @param apiVersion the api version + * @return TitanVertex + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex getUniqueNode( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> keyPropsHash, TitanVertex depNodeVal, String apiVersion ) throws AAIException{ + + // NOTE - this is really for use by the PersistNode method -- it is looking to see if + // a node exists in the database given either Primary or Alternate Key data and dependent + // node data (if required for uniqueness). + + // Note - the passed in nodeType could really be a nodeTypeCategory --- + Boolean nodeTypeIsCategory = DbEdgeRules.NodeTypeCategory.containsKey(nodeType); + + Boolean useDepNode = false; + if( needsADepNode4Uniqueness(transId, fromAppId, nodeType, apiVersion) ){ + // This kind of node depends on another node for uniqueness + if( depNodeVal == null ){ + // They should have passed in the node that this one depends on + throw new AAIException("AAI_6109", "null dependentNode object passed to getUniqueNode() but " + nodeType + " requires one."); + } + else if( ! nodeTypeACanDependOnB(transId, fromAppId, nodeType, depNodeVal.<String>property("aai-node-type").orElse(null), apiVersion) ){ + // They should have passed in the right type of node as the dependent node + throw new AAIException("AAI_6109", "dependentNode of type " + depNodeVal.<String>property("aai-node-type").orElse(null) + " passed to getUniqueNode() for nodeType" + nodeType + ".\n"); + } + useDepNode = true; + } + else { + depNodeVal = null; + } + + // We assume that all NodeTypes have at least one key-property defined. A dependentNode is optional. + // Note - instead of key-properties (the primary key properties), a user could pass + // alternate-key values if they are defined for the nodeType. + ArrayList<String> kName = new ArrayList<String>(); + ArrayList<Object> kVal = new ArrayList<Object>(); + + Collection <String> keyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion); + Iterator <String> keyPropI = keyProps.iterator(); + Boolean haveSomePrimKeyProps = false; + Boolean primaryKeyComplete = true; + while( keyPropI.hasNext() ){ + haveSomePrimKeyProps = true; + + String propName = keyPropI.next(); + if( ! keyPropsHash.containsKey(propName) ){ + primaryKeyComplete = false; + } + else { + Object valObj = keyPropsHash.get(propName); + if( valObj == null ){ + primaryKeyComplete = false; + } + else { + String value = valObj.toString(); + if( value == null || value.equals("") ){ + // They passed the property name, but no value + primaryKeyComplete = false; + } + } + } + } + + int i = -1; + if( haveSomePrimKeyProps && primaryKeyComplete ){ + keyPropI = keyProps.iterator(); + while( keyPropI.hasNext() ){ + String propName = keyPropI.next(); + String value = (keyPropsHash.get(propName)).toString(); + i++; + kName.add(i, propName); + kVal.add(i, (Object)value); + } + } + else { + // See if they're using the alternate key + Collection <String> altKey1Props = getNodeAltKey1PropNames(transId, fromAppId, nodeType, apiVersion); + Iterator <String> altKey1PropI = altKey1Props.iterator(); + Boolean haveSomeAltKey1Props = false; + Boolean altKey1Complete = true; + while( altKey1PropI.hasNext() ){ + haveSomeAltKey1Props = true; + String propName = altKey1PropI.next(); + if( ! keyPropsHash.containsKey(propName) ){ + altKey1Complete = false; + } + else { + Object valObj = keyPropsHash.get(propName); + if( valObj == null ){ + altKey1Complete = false; + } + else { + String value = valObj.toString(); + if( value == null || value.equals("") ){ + // They passed the property name, but no value + altKey1Complete = false; + } + } + } + } + if( haveSomeAltKey1Props && altKey1Complete ){ + altKey1PropI = altKey1Props.iterator(); + while( altKey1PropI.hasNext() ){ + String propName = altKey1PropI.next(); + String value = (keyPropsHash.get(propName)).toString(); + i++; + kName.add(i, propName); + kVal.add(i, (Object)value); + } + } + } + + int topPropIndex = i; + TitanVertex tiV = null; + String propsAndValuesForMsg = ""; + if( !useDepNode ){ + // There is no node that this type of node depends on, so we can look for it based + // solely on the Aai-defined key fields. + Iterable <?> verts = null; + + if( topPropIndex == -1 ){ + // Problem if no key Properties defined for this nodeType + String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + throw new AAIException("AAI_6105", "Bad or Incomplete Key Property params: (" + keyPropsHash.toString() + + ") for nodeType: " + nodeType + " (ver=" + defVer + ")"); + } + else if( topPropIndex == 0 ){ + if (nodeTypeIsCategory) // dont know real type + verts= graph.query().has(kName.get(0),kVal.get(0)).vertices(); + else // need this to find dvs switch: dvs.switch-name and port-group.switch-name issue + verts= graph.query().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") "; + } + else if( topPropIndex == 1 ){ + verts = graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ") "; + } + else if( topPropIndex == 2 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ") "; + } + else if( topPropIndex == 3 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ", " + + kName.get(3) + " = " + kVal.get(3) + ") "; + } + else { + String emsg = " We only support 4 keys per nodeType for now \n"; + throw new AAIException("AAI_6114", emsg); + } + + Iterator <?> vertI = verts.iterator(); + if( vertI != null && vertI.hasNext()) { + // We found a vertex that meets the input criteria. + tiV = (TitanVertex) vertI.next(); + + if( vertI.hasNext() ){ + // Since this routine is looking for a unique node for the given input values, if + // more than one is found - it's a problem. + throw new AAIException("AAI_6112", "More than one Node found by getUniqueNode for params: " + propsAndValuesForMsg); + } + } + else { + // No Vertex was found for this key - throw a not-found exception + throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg); + } + } + else { + // Need to use the dependent vertex to look for this one. + // filter this to the actual keys because + HashMap<String,Object> onlyKeysHash = new HashMap<String,Object>(); + + Collection <String> onlyKeyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion); + + Iterator <String> onlyKeyPropsI = onlyKeyProps.iterator(); + + while( onlyKeyPropsI.hasNext() ){ + String keyName = onlyKeyPropsI.next(); + onlyKeysHash.put(keyName, keyPropsHash.get(keyName)); + } + + propsAndValuesForMsg = onlyKeysHash.toString() + " combined with a Dependent [" + depNodeVal.<String>property("aai-node-type").orElse(null) + "] node."; + ArrayList<TitanVertex> resultList = DbMeth.getConnectedNodes(transId, fromAppId, graph, nodeType, onlyKeysHash, + depNodeVal, apiVersion, false); + if( resultList.size() > 1 ){ + // More than one vertex found when we thought there should only be one. + throw new AAIException("AAI_6112", "More than one Node found by getUniqueNode for params: " + propsAndValuesForMsg); + } + else if( resultList.size() == 1 ){ + tiV = resultList.get(0); + } + } + + if( tiV == null ){ + // No Vertex was found for this key - throw a not-found exception + throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg); + } + else { + if( !DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + // The nodeType passed in was a real one, not a nodeTypeCategory, so we will + // use it as part of the query to make sure we find the right type of node. + // This can be an issue if they're using nodeTypes covered by a nodeTypeCategory but + // pass in the wrong nodeType. We don't want them to ask for one thing and get the other. + String foundNodeType = tiV.<String>property("aai-node-type").orElse(null); + if( foundNodeType != null && !foundNodeType.equals(nodeType) ){ + throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg + " (did find a " + foundNodeType + " though.)"); + } + } + + return tiV; + } + + }// End of getUniqueNode() + + /** + * Gets the unique node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param keyPropsHash the key props hash + * @param depNodeVal the dep node val + * @return the unique node + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex getUniqueNode( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> keyPropsHash, TitanVertex depNodeVal) throws AAIException { + return getUniqueNode( transId, fromAppId, graph, nodeType, + keyPropsHash, depNodeVal, null ); + } + // End getUniqueNode() + + + /** + * Gets the unique node with dep params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param nodePropsHash the node props hash + * @param apiVersion the api version + * @return TitanVertex + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex getUniqueNodeWithDepParams( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> nodePropsHash, String apiVersion ) + throws AAIException{ + /* + * This method uses the nodePropsHash to walk back over dependent nodes until it finds one that + * does not depend on any other for uniqueness. It uses the getUniqueNode() method as it finds + * dependent nodes. NOTE -- it is passed a hash of all the nodeProperties -- for itself and + * for any dependent nodes that it will need to find. There are some types of nodes that can + * depend on more than one node, we assume that there wouldn't be a case where BOTH types of + * dependent nodes are in the trail that we need to traverse. Ie. an ipaddress can depend on + * either a vserver or pserver. NOTE this case can now happen -- nodePropsHash + * should now be sent as a LinkedHashMap in this case so we can search in order. + */ + + // NOTE ALSO -- We're currently supporting 6 layers of dependency. We never thought there would be this + // many layers before hitting a node-type that would be uniquely identifiable on it's own. So the + // code is a little ugly with all these nested if-then-else's. Since we're supporting so many + // layers, it should be re-written so we can support "n" layers instead of having to go in hear + // and adding code... But as of 15-07, we really don't NEED more than 5. + + // NOTE: The passed in nodeType could really be a nodeTypeCategory -- + // The calls to figureDepNodeTypeForRequest() below will deal with it for the dep nodes, the + // call to getUniqueNode() takes care of it for the node itself. + + TitanVertex nullVert = null; + String depNodeType = figureDepNodeTypeForRequest( transId, fromAppId, nodeType, nodePropsHash, apiVersion ); + if( depNodeType.equals("")){ + // This kind of node does not depend on another node for uniqueness, so + // we can just use the "getUniqueNode()" method to get it. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, nullVert, apiVersion) ); + } + else { + // Will need to find the second-layer dependent node + String secondLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, depNodeType, nodePropsHash, apiVersion ); + if( secondLayerDepNodeType.equals("")){ + // This second-layer kind of node does not depend on another node for uniqueness. + // So once we find the second-layer node, we can use it to get the top-layer guy. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion); + TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, nullVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) ); + } + else { + // Will need to find the third-layer dependent node + /// String thirdLayerDepNodeType = dbMaps.NodeDependencies.get(secondLayerDepNodeType); + String thirdLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion ); + + if( thirdLayerDepNodeType.equals("")){ + // This third-layer kind of node does not depend on another node for uniqueness. + // So we can find it, and then use it to find the second-layer and then use that to find the top guy. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion); + TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) ); + } + else { + // Will need to find the third-layer dependent node + String forthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion ); + if( forthLayerDepNodeType == null || forthLayerDepNodeType.equals("")){ + // This forth-layer kind of node does not depend on another node for uniqueness. + // So we can find it, and then use it to find the third, then second-layer and then use that to find the top guy. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion); + TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) ); + } + else { + // Will need to find the forth-layer dependent node + String fifthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion ); + if( fifthLayerDepNodeType == null || fifthLayerDepNodeType.equals("")){ + // This fifth-layer kind of node does not depend on another node for uniqueness. + // So we can find it, and then use it to find the forth, third, then second-layer and then use that to find the top guy. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex fifthLayerDepVert = getUniqueNode(transId, fromAppId, graph, forthLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, fifthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion); + TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) ); + } + else { + // Will need to find the fifth-layer dependent node + String sixthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, fifthLayerDepNodeType, nodePropsHash, apiVersion ); + if( sixthLayerDepNodeType == null || sixthLayerDepNodeType.equals("")){ + // This six-layer kind of node does not depend on another node for uniqueness. + // So we can find it, and then use it to find the fifth, forth, third, then second-layer and then use that to find the top guy. + HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, fifthLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex sixthLayerDepVert = getUniqueNode(transId, fromAppId, graph, fifthLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex fifthLayerDepVert = getUniqueNode(transId, fromAppId, graph, forthLayerDepNodeType, thisNodeTypeParamHash, sixthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, fifthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion); + TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion); + TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion); + + thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion); + return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) ); + } + else { + // We don't currently support more layers. We can later if we need to. + // Hopefully, we'll never need to go this deep -- there should be unique keys in there somewhere! + throw new AAIException("AAI_6114", "CODE-LIMITATION - Can't resolve dependant node layers for nodeType = " + nodeType); + } + } + } + } + } + } + } // End getUniqueNodeWithDepParams() + + /** + * Gets the unique node with dep params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param nodePropsHash the node props hash + * @return the unique node with dep params + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanVertex getUniqueNodeWithDepParams( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> nodePropsHash ) throws AAIException { + return getUniqueNodeWithDepParams(transId, fromAppId, graph, nodeType, nodePropsHash, null); + } + + + /** + * Gets the this node type params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param targetNodeType the target node type + * @param passedHash the passed hash + * @param apiVersion the api version + * @return the this node type params + * @throws AAIException the AAI exception + */ + private static HashMap <String, Object> getThisNodeTypeParams(String transId, String fromAppId, String targetNodeType, + HashMap<String,Object> passedHash, String apiVersion )throws AAIException{ + /* + * For the passed-in hash, each key is assumed to look like, "nodeType.paramName". We want to + * pick out the entries that match the targetNodeType and return those with the values they go with. The + * returned keys will have the "nodeType." stripped off. + * + * NOTE - the nodeType passed to this method could actually be a nodeTypeCategory. Just keepin it ugly. + */ + + if( passedHash == null ){ + throw new AAIException("AAI_6120", "Bad param: null passedHash "); + } + + String targetNodeTypeCat = ""; + if( DbEdgeRules.NodeTypeCatMap.containsKey(targetNodeType) ){ + targetNodeTypeCat = DbEdgeRules.NodeTypeCatMap.get(targetNodeType); + } + + HashMap <String,Object> returnHash = new HashMap <String,Object> (); + Iterator <Map.Entry<String,Object>>it = passedHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry <String,Object>pairs = (Map.Entry<String,Object>)it.next(); + String k = (pairs.getKey()).toString(); + int periodLoc = k.indexOf("."); + if( periodLoc <= 0 ){ + throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + k + "]. Expected format = [nodeName.paramName]\n"); + } + else { + String nty = k.substring(0,periodLoc); + String paramName = k.substring(periodLoc + 1); + if( nty.equals(targetNodeType) || (!targetNodeTypeCat.equals("") && nty.equals(targetNodeTypeCat)) ){ + String newK = paramName; + returnHash.put( newK,pairs.getValue() ); + } + } + } + + //aaiLogger.debug(logline, " - end "); + return returnHash; + + }// End of getThisNodeTypeParams() + + /** + * Gets the this node type params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param targetNodeType the target node type + * @param passedHash the passed hash + * @return the this node type params + * @throws AAIException the AAI exception + */ + @Deprecated + private static HashMap <String, Object> getThisNodeTypeParams(String transId, String fromAppId, String targetNodeType, + HashMap<String,Object> passedHash )throws AAIException{ + return getThisNodeTypeParams( transId, fromAppId, targetNodeType, + passedHash, null); + + } + + /** + * Gets the dep node types. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return the dep node types + * @throws AAIException the AAI exception + */ + public static ArrayList <String> getDepNodeTypes(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{ + /* + * This returns any nodeTypes that this nodeType can be dependent on. A particular instance of a node will only + * depend on one other node - we don't currently support dependence on multiple nodes. + */ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + ArrayList <String> depNodeTypeL = new ArrayList <String> (); + if( !DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + // This is a good-ole nodeType + Collection <String> depNTColl = dbMaps.NodeDependencies.get(nodeType); + Iterator <String> ntItr = depNTColl.iterator(); + while( ntItr.hasNext() ){ + depNodeTypeL.add(ntItr.next()); + } + } + else { + // The passed-in nodeType must really be a nodeTypeCategory + Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType); + Iterator <String> catItr = nTypeCatCol.iterator(); + String catInfo = ""; + if( catItr.hasNext() ){ + // For now, we only look for one. + catInfo = catItr.next(); + } + else { + throw new AAIException("AAI_6121", "Error getting DbEdgeRules.NodeTypeCategory info for nodeTypeCat = " + nodeType); + } + + String [] flds = catInfo.split(","); + if( flds.length != 4 ){ + throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); + } + + String nodeTypesString = flds[0]; + String hasDepNodes = flds[3]; + if( hasDepNodes.equals("true") ){ + String [] ntNames = nodeTypesString.split("\\|"); + for( int i = 0; i < ntNames.length; i++ ){ + Collection <String> depNTColl = dbMaps.NodeDependencies.get(nodeType); + Iterator <String> ntItr = depNTColl.iterator(); + while( ntItr.hasNext() ){ + String depNode = ntItr.next(); + if( !depNodeTypeL.contains(depNode) ){ + depNodeTypeL.add(depNode); + } + } + } + } + } + + + return depNodeTypeL; + + }// End getDepNodeTypes() + + /** + * Gets the dep node types. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @return the dep node types + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList <String> getDepNodeTypes(String transId, String fromAppId, String nodeType)throws AAIException{ + return getDepNodeTypes( transId, fromAppId, nodeType, null); + } + + /** + * Gets the default delete scope. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return the default delete scope + * @throws AAIException the AAI exception + */ + private static String getDefaultDeleteScope(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{ + + // At some point we may have different delete rules for different services, so this is + // a list for now even thought there's only one scope per nodeType. + Collection <String> scopeList = DbEdgeRules.DefaultDeleteScope.get(nodeType); + if( scopeList.isEmpty() ){ + throw new AAIException("AAI_6121", "No default deleteScope found for nodeType = [" + nodeType + "] "); + } + else { + Iterator <String> ito = scopeList.iterator(); + return ito.next(); + } + + }// End getDefaultDeleteScope() + + /** + * Gets the default delete scope. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @return the default delete scope + * @throws AAIException the AAI exception + */ + @Deprecated + private static String getDefaultDeleteScope(String transId, String fromAppId, String nodeType)throws AAIException{ + return getDefaultDeleteScope( transId, fromAppId, nodeType, null); + } + + /** + * Needs A dep node 4 uniqueness. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return the boolean + * @throws AAIException the AAI exception + */ + public static Boolean needsADepNode4Uniqueness(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{ + // Note: the passed in nodeType could really be a nodeTypeCategory. That is handled by getDepNodeTypes() + + ArrayList <String> depList = getDepNodeTypes(transId, fromAppId, nodeType, apiVersion); + if( depList.isEmpty() ){ + return false; + } + else { + return true; + } + + }// End needsADepNode4Uniqueness() + + /** + * Needs A dep node 4 uniqueness. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @return the boolean + * @throws AAIException the AAI exception + */ + @Deprecated + private static Boolean needsADepNode4Uniqueness(String transId, String fromAppId, String nodeType)throws AAIException{ + return needsADepNode4Uniqueness( transId, fromAppId, nodeType, null); + } + + /** + * Node type A can depend on B. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeTypeA the node type A + * @param nodeTypeB the node type B + * @param apiVersion the api version + * @return the boolean + * @throws AAIException the AAI exception + */ + public static Boolean nodeTypeACanDependOnB(String transId, String fromAppId, String nodeTypeA, String nodeTypeB, String apiVersion) + throws AAIException{ + // Note: the passed in nodeType could really be a nodeTypeCategory. That is handled by getDepNodeTypes() + + ArrayList <String> depList = getDepNodeTypes(transId, fromAppId, nodeTypeA, apiVersion); + if( depList.isEmpty() ){ + return false; + } + else if( depList.contains(nodeTypeB) ){ + return true; + } + else { + return false; + } + + }// End nodeTypeACanDependOnB() + + /** + * Node type A can depend on B. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeTypeA the node type A + * @param nodeTypeB the node type B + * @return the boolean + * @throws AAIException the AAI exception + */ + @Deprecated + private static Boolean nodeTypeACanDependOnB(String transId, String fromAppId, String nodeTypeA, String nodeTypeB) + throws AAIException{ + return nodeTypeACanDependOnB( transId, fromAppId, nodeTypeA, nodeTypeB, null); + } + + /** + * Figure dep node type for request. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param requestParamHash the request param hash + * @param apiVersion the api version + * @return the string + * @throws AAIException the AAI exception + */ + public static String figureDepNodeTypeForRequest(String transId, String fromAppId, String nodeType, + HashMap<String,Object> requestParamHash, String apiVersion )throws AAIException{ + /* + * This is ugly. But if the passed-in nodeType is dependent on another nodeType for + * uniqueness, we need to return what that dependent node-type is. The ugly comes in + * because a node can be dependent on more than one type of node. So, to tell which one + * is going to apply, we root through the passed request parameters to see which of + * the possible dependent node types is being used. + * Note -- if there comes a day when there are so many dependencies that the request could + * have more than one that match -- Then we need to think up something new. But for now, + * we have to assume that if there are more than one legal dep-node-types, only one will + * be represented in the requestHash data. >>> NOTE >>> That day has come. For + * the upstreamers will send in a LinkedHashMap instead of just an unordered + * HashMap so we can look in order for the dependent node. + * + */ + + if( requestParamHash == null ){ + throw new AAIException("AAI_6120", "Bad param: null requestParamHash "); + } + + ArrayList <String> depNodeTypes = getDepNodeTypes(transId, fromAppId, nodeType, apiVersion); + if( depNodeTypes.isEmpty() ){ + // This kind of node is not dependent on any other + //aaiLogger.debug(logline, " (not dependent) - end "); + return ""; + } + else if( depNodeTypes.size() == 1 ){ + // This kind of node can only depend on one other nodeType - so return that. + //aaiLogger.debug(logline, " (depends on " + depNodeTypes.get(0) + " - end "); + return depNodeTypes.get(0); + } + else { + // We need to look to find the first of the dep-node types that is represented in the passed-in + // request data. That will be the one we need to use. + + // first find out what node-types are represented in the requestHash + + Iterator <Map.Entry<String,Object>>it = requestParamHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry <String,Object>pairs = (Map.Entry<String,Object>)it.next(); + String k = (pairs.getKey()).toString(); + int periodLoc = k.indexOf("."); + if( periodLoc <= 0 ){ + throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + k + "]. Expected format = [nodeName.paramName]\n"); + } + else { + String nty = k.substring(0,periodLoc); + if( depNodeTypes.contains(nty) ){ + // This is the first possible dep. node type we've found for the passed in data set + return nty; + } + } + } + + } + + // It's not an error if none is found - the caller needs to deal with cases where there + // should be a dep. node identified but isn't. + //aaiLogger.debug(logline, " no dep NT found - end "); + return ""; + + }// End of figureDepNodeTypeForRequest() + + /** + * Figure dep node type for request. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param requestParamHash the request param hash + * @return the string + * @throws AAIException the AAI exception + */ + @Deprecated + public static String figureDepNodeTypeForRequest(String transId, String fromAppId, String nodeType, + HashMap<String,Object> requestParamHash )throws AAIException{ + return figureDepNodeTypeForRequest( transId, fromAppId, nodeType, requestParamHash, null); + } + + /** + * Detach connected nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param startNodeVal the start node val + * @param autoDeleteOrphans the auto delete orphans + * @param apiVersion the api version + * @return deletedNodeCount + * @throws AAIException the AAI exception + */ + public static int detachConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, boolean autoDeleteOrphans, String apiVersion ) throws AAIException{ + + /* Find nodes that are attached to this node which meet the nodeType/filterParams criteria. + * Remove the edges that go to those nodes. + * If that turns any of the nodes into an orphan, then delete it if the autoDeleteOrphans flag is set. + * Return a count of how many nodes were actually deleted (not just detached). + */ + + int deletedCount = 0; + + if( startNodeVal == null ){ + // They should have passed in the node that this query starts from + throw new AAIException("AAI_6109", "null startNode object passed to detachConnectedNodes()."); + } + + // We want to loop through the connected Nodes that we found. + // For each connected Node, we'll get the all edges that start from that node and look for the one + // that connects back to our startNode. + // Only delete the edge that connects back to our startNode. + // then autoDeleteOrphans flag is set, then delete the connectedNode if it's now orphaned. + // + + String startNodeVId = startNodeVal.id().toString(); + ArrayList<TitanVertex> conNodeList = getConnectedNodes( transId, fromAppId, graph, nodeType, propFilterHash, startNodeVal, apiVersion, false ); + Iterator<TitanVertex> conVIter = conNodeList.iterator(); + while( conVIter.hasNext() ){ + TitanVertex connectedVert = conVIter.next(); + boolean isFirstOne = true; + Iterator<Edge> eI = connectedVert.edges(Direction.BOTH); + while( eI.hasNext() ){ + TitanEdge ed = (TitanEdge) eI.next(); + TitanVertex otherVtx = (TitanVertex) ed.otherVertex(connectedVert); + String otherSideLookingBackVId = otherVtx.id().toString(); + if( startNodeVId.equals(otherSideLookingBackVId) ){ + // This is an edge from the connected node back to our starting node + if( isFirstOne && !eI.hasNext() && autoDeleteOrphans ){ + // This was the one and only edge for this connectedNode, so + // delete the node and edge since flag was set + String resVers = connectedVert.<String>property("resource-version").orElse(null); + removeAaiNode( transId, fromAppId, graph, connectedVert, "USE_DEFAULT", apiVersion, resVers); + deletedCount = deletedCount + 1; + } + else { + removeAaiEdge( transId, fromAppId, graph, ed ); + } + } + isFirstOne = false; + } + } + return deletedCount; + + } // end of detachConnectedNodes() + + + + /** + * Detach connected nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param startNodeVal the start node val + * @param autoDeleteOrphans the auto delete orphans + * @return the int + * @throws AAIException the AAI exception + */ + @Deprecated + public static int detachConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, boolean autoDeleteOrphans ) throws AAIException{ + return detachConnectedNodes( transId, fromAppId, graph, nodeType, + propFilterHash, startNodeVal, autoDeleteOrphans, null); + } + + /** + * Gets the nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param noFilterOnPurpose the no filter on purpose + * @param apiVersion the api version + * @return ArrayList<TitanVertex> + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose, String apiVersion ) throws AAIException{ + boolean skipGroomingFlag = true; + // we will only do real-time grooming if a system variable is set, telling us not to skip it. + String skipGroomingStr = AAIConstants.AAI_SKIPREALTIME_GROOMING; + if( skipGroomingStr.equals("false") ){ + skipGroomingFlag = false; + } + return( getNodes(transId, fromAppId, graph, nodeType, propFilterHash, noFilterOnPurpose, apiVersion, skipGroomingFlag) ); + } + + /** + * Gets the nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param noFilterOnPurpose the no filter on purpose + * @param apiVersion the api version + * @param skipGroomCheck the skip groom check + * @return ArrayList<TitanVertex> + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose, String apiVersion, boolean skipGroomCheck ) + throws AAIException{ + // Note - the skipGroomCheck flag is set to true when the DataGrooming tool is using this method to collect + // node data. When the grooming tool is collecting data, we don't want any nodes skipped, because we + // want details about what nodes/edges are bad - more detail than the check in this method does + // as it checks if a node is ok to return to a caller. + + /* Use the nodeType + filterParams to find nodes. + */ + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + ArrayList<TitanVertex> returnVertList = new ArrayList<TitanVertex>(); + if( nodeType == null || nodeType.equals("") ){ + // They should have passed in a nodeType + throw new AAIException("AAI_6118", "Required field: nodeType not passed to getNodes()."); + } + + if( !noFilterOnPurpose && (propFilterHash == null || propFilterHash.isEmpty()) ){ + // They should have passed at least one property to filter on + throw new AAIException("AAI_6118", "Required field: propFilterHash not passed to getNodes()."); + } + + ArrayList<String> kName = new ArrayList<String>(); + ArrayList<Object> kVal = new ArrayList<Object>(); + int i = -1; + Collection <String> indexedProps = dbMaps.NodeMapIndexedProps.get(nodeType); + // First loop through to pick up the indexed-properties if there are any being used + + if( propFilterHash != null ){ + Iterator <?> it = propFilterHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry<?,?> propEntry = (Map.Entry<?,?>) it.next(); + String propName = (propEntry.getKey()).toString(); + // Don't allow search on properties that do not have SINGLE cardinality + if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List") ){ + if( indexedProps.contains(propName) ){ + i++; + kName.add(i, propName); + kVal.add(i, (Object)propEntry.getValue()); + } + } + } + + // Now go through again and pick up the non-indexed properties + it = propFilterHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry <?,?> propEntry = (Map.Entry<?,?>)it.next(); + String propName = (propEntry.getKey()).toString(); + // Don't allow search on properties that do not have SINGLE cardinality + if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List") ){ + if( ! indexedProps.contains(propName) ){ + i++; + kName.add(i, propName); + kVal.add(i, (Object)propEntry.getValue()); + } + } + } + } + + Iterable <?> verts = null; + String propsAndValuesForMsg = ""; + int topPropIndex = i; + if( topPropIndex == -1 ){ + // No Filtering -- just go get them all + verts= graph.query().has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " ( no filter props ) "; + } + else if( topPropIndex == 0 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") "; + } + else if( topPropIndex == 1 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ") "; + } + else if( topPropIndex == 2 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ") "; + } + else if( topPropIndex == 3 ){ + verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).has("aai-node-type",nodeType).vertices(); + propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " + + kName.get(1) + " = " + kVal.get(1) + ", " + + kName.get(2) + " = " + kVal.get(2) + ", " + + kName.get(3) + " = " + kVal.get(3) + ") "; + } + else { + String emsg = " -- Sorry -- we only support 4 filter properties in getNodes() for now... \n"; + throw new AAIException("AAI_6114", emsg); + } + if( verts != null ){ + // We did find some matching vertices + Iterator <?> it = verts.iterator(); + while( it.hasNext() ){ + TitanVertex v = (TitanVertex)it.next(); + + if( skipGroomCheck ){ + // Good or bad, just return everything we find + returnVertList.add( v ); + } + else { + // Weed out any bad vertices we find + if( thisVertexNotReachable(transId, fromAppId, graph, v, apiVersion) ){ + LOGGER.info("IN-LINE GROOMING - Unreachable Node DETECTED > skipping it. "); + } + else if( thisVertexHasBadEdges(transId, fromAppId, graph, v, apiVersion) ){ + LOGGER.info("IN-LINE GROOMING - BAD EDGE DETECTED > skipping vtxId = [" + v.id() + "] "); + } + else if( thisVertexIsAPhantom(transId, fromAppId, graph, v, apiVersion) ){ + LOGGER.info("IN-LINE GROOMING - BAD NODE DETECTED > skipping vtxId = [" + v.id() + "] "); + } + else { + returnVertList.add( v ); + } + } + } + } + + return returnVertList; + } + + /** + * Gets the nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param noFilterOnPurpose the no filter on purpose + * @return the nodes + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose ) throws AAIException{ + return getNodes(transId, fromAppId, graph, nodeType, + propFilterHash, noFilterOnPurpose, null ); + } + // End of getNodes() + + + /** + * Gets the connected children. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVtx the start vtx + * @param limitToThisNodeType the limit to this node type + * @return ArrayList <TitanVertex> + * @throws AAIException the AAI exception + */ + public static ArrayList<TitanVertex> getConnectedChildren( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVtx, String limitToThisNodeType ) throws AAIException{ + + // Just get child nodes (ie. other end of an OUT edge that is tagged as a parent/Child edge) + + ArrayList <TitanVertex> childList = new ArrayList <TitanVertex> (); + Boolean doNodeTypeCheck = false; + if( limitToThisNodeType != null && ! limitToThisNodeType.equals("") ){ + doNodeTypeCheck = true; + } + + + List<Vertex> verts = graph.traversal().V(startVtx).union(__.outE().has("isParent-REV", true).inV(), __.inE().has("isParent", true).outV()).toList(); + TitanVertex tmpVtx = null; + int vertsSize = verts.size(); + for (int i = 0; i < vertsSize; i++){ + tmpVtx = (TitanVertex) verts.get(i); + if( ! doNodeTypeCheck ){ + childList.add(tmpVtx); + } + else { + String tmpNT = tmpVtx.<String>property("aai-node-type").orElse(null); + if( tmpNT != null && tmpNT.equals(limitToThisNodeType) ){ + childList.add(tmpVtx); + } + } + } + + return childList; + + }// End of getConnectedChildren() + + + + /** + * Gets the connected nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param startNodeVal the start node val + * @param apiVersion the api version + * @param excludeRecurComingIn the exclude recur coming in + * @return ArrayList <TitanVertex> + * @throws AAIException the AAI exception + */ + public static ArrayList<TitanVertex> getConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, String apiVersion, Boolean excludeRecurComingIn ) throws AAIException{ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + /* Get (almost) all the nodes that are connected to this vertex. + * Narrow down what is returned using optional filter parameters nodeType and propFilterHash + * NOTE - the default behavior has changed slightly. For start-Nodes that + * can be recursivly connected, this method will only bring back the same kind of + * connected node by following an OUT edge. Ie. if the start node is an "model-element", + * then this method will only follow OUT edges to get to other "model-element" type nodes. + */ + + String startNodeNT = ""; + if( startNodeVal == null ){ + // They should have passed in the node that this query starts from + throw new AAIException("AAI_6109", "null startNode object passed to getConnectedNodes()."); + } + else { + startNodeNT = startNodeVal.<String>property("aai-node-type").orElse(null); + } + + boolean nodeTypeFilter = false; + if( nodeType != null && !nodeType.equals("") ){ + // They want to filter using nodeType + if( ! dbMaps.NodeProps.containsKey(nodeType) ){ + throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to getConnectedNodes()."); + } + nodeTypeFilter = true; + } + + ArrayList <String> excludeVidList = new <String> ArrayList (); + if( DbEdgeRules.CanBeRecursiveNT.containsKey(startNodeNT) && excludeRecurComingIn ){ + // If we're starting on a nodeType that supports recursion, then find any connected + // nodes that are coming from IN edges so we can exclude them later. + + Iterable <?> vertsR = startNodeVal.query().direction(Direction.IN).vertices(); + Iterator <?> vertIR = vertsR.iterator(); + while( vertIR != null && vertIR.hasNext() ){ + TitanVertex tmpVertIN = (TitanVertex) vertIR.next(); + String tmpNT = tmpVertIN.<String>property("aai-node-type").orElse(null); + if( tmpNT != null && tmpNT.equals(startNodeNT) ){ + // We're on a nodetype that supports recursion (like model-element) and we've + // found an connected Node of this same type on an IN edge - put this + // on our excludeList. + excludeVidList.add( tmpVertIN.id().toString() ); + } + } + } + + boolean propertyFilter = false; + if( propFilterHash != null && !propFilterHash.isEmpty() ){ + // They want to filter using some properties + Iterator <?> it = propFilterHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry<?,?> propEntry = (Map.Entry<?,?>)it.next(); + String propName = (propEntry.getKey()).toString(); + if( ! dbMaps.NodeProps.containsValue(propName) ){ + throw new AAIException("AAI_6116", "Unrecognized property name [" + propName + "] passed to getConnectedNodes()."); + } + // Don't allow search on properties that do not have SINGLE cardinality + if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List") ){ + propertyFilter = true; + } + } + } + // If filter-properties were passed in, then look for nodes that have those values. + ArrayList<TitanVertex> returnVertList = new ArrayList<TitanVertex>(); + Iterable<TitanVertex> qResult = null; + Iterator<TitanVertex> resultI = null; + try { + qResult = startNodeVal.query().vertices(); + resultI = qResult.iterator(); + } + catch( NullPointerException npe ){ + throw new AAIException("AAI_6125", "Titan null pointer exception trying to get nodes connected to vertexId = " + + startNodeVal.id() + ", aai-node-type = [" + startNodeVal.property("aai-node-type") + "]."); + } + + while( resultI != null && resultI.hasNext() ){ + boolean addThisOne = true; + TitanVertex tmpV = (TitanVertex)resultI.next(); + if( tmpV == null ){ + LOGGER.info("Titan gave a null vertex when looking for nodes connected to vertexId = " + + startNodeVal.id() + ", aai-node-type = [" + startNodeVal.property("aai-node-type") + "]."); + // Note - we will skip this one, but try to return any others that we find. + addThisOne = false; + } + + else { + String tmpVid = tmpV.id().toString(); + if( nodeTypeFilter ){ + Object nto = tmpV.<Object>property("aai-node-type").orElse(null); + if( nto == null || !nto.toString().equals(nodeType) ){ + //LOGGER.info("Found a connected vertex (vertexId = " + + // tmpVid + "), but we will not collect it. It had aai-node-type [" + + // nto + "], we are looking for [" + nodeType + "]. "); + // Note - we will skip this one, but try to return any others that we find. + addThisOne = false; + } + } + + if( excludeVidList.contains(tmpVid) ){ + LOGGER.info("Found a connected vertex (vertexId = " + + tmpVid + "), but will exclude it since it is on an IN edge and this nodeType " + + startNodeNT + " can be recursively attached."); + // Note - we will skip this one, but try to return any others that we find. + addThisOne = false; + } + + if( propertyFilter ){ + Iterator <?> it = propFilterHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry <?,?>propEntry = (Map.Entry<?,?>)it.next(); + String propName = (propEntry.getKey()).toString(); + if( checkPropCardinality(propName, "Set") || checkPropCardinality(propName, "List") ){ + // Don't allow search on properties that do not have SINGLE cardinality + continue; + } + Object propVal = propEntry.getValue(); + Object foundVal = tmpV.<Object>property(propName).orElse(null); + if( foundVal != null && propVal != null && !foundVal.toString().equals(propVal.toString()) ){ + addThisOne = false; + break; + } + else if( (foundVal == null && propVal != null) || (foundVal != null && propVal == null) ){ + addThisOne = false; + break; + } + } + } + } + if( addThisOne ){ + // This node passed the tests -- put it on the return List + returnVertList.add( (TitanVertex)tmpV ); + } + } + //aaiLogger.debug(logline, " end "); + return returnVertList; + + }// End of getConnectedNodes() + + + /** + * Gets the connected nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param startNodeVal the start node val + * @param apiVersion the api version + * @return the connected nodes + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList<TitanVertex> getConnectedNodes(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, String apiVersion ) throws AAIException { + return getConnectedNodes( transId, fromAppId, graph, nodeType, + propFilterHash, startNodeVal, apiVersion, true ); + } + + /** + * Gets the connected nodes. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param nodeType the node type + * @param propFilterHash the prop filter hash + * @param startNodeVal the start node val + * @return the connected nodes + * @throws AAIException the AAI exception + */ + @Deprecated + public static ArrayList<TitanVertex> getConnectedNodes(String transId, String fromAppId, TitanTransaction graph, String nodeType, + HashMap<String,Object> propFilterHash, TitanVertex startNodeVal ) throws AAIException { + return getConnectedNodes( transId, fromAppId, graph, nodeType, + propFilterHash, startNodeVal, null, true ); + + } + + /** + * Ip address format OK. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param addrVal the addr val + * @param addrVer the addr ver + * @param apiVersion the api version + * @return Boolean + * @throws AAIException the AAI exception + */ + public static Boolean ipAddressFormatOK(String transId, String fromAppId, String addrVal, String addrVer, String apiVersion) throws AAIException{ + + /* NOTE -- the google methods we use do not allow leading zeros in ipV4 addresses. + * So it will reject, "22.33.44.001" + */ + + if( addrVal == null ){ + throw new AAIException("AAI_6120", "Bad data (addrVal = null) passed to ipAddressFormatOK()"); + } + else if( addrVer == null ){ + throw new AAIException("AAI_6120", "Bad data (addrType = null) passed to ipAddressFormatOK()"); + } + + Boolean retVal = false; + Boolean lookingForV4 = false; + Boolean lookingForV6 = false; + InetAddress inetAddr = null; + + if( addrVer.equalsIgnoreCase("v4") || addrVer.equals("ipv4") || addrVer.equals("4")){ + lookingForV4 = true; + } + else if( addrVer.equalsIgnoreCase("v6") || addrVer.equals("ipv6") || addrVer.equals("6")){ + lookingForV6 = true; + } + else { + throw new AAIException("AAI_6120", " Bad data for addressVersion [" + addrVer + "] passed to ipAddressFormatOK()"); + } + + try { + inetAddr = InetAddresses.forString(addrVal); + if( inetAddr instanceof Inet4Address ){ + if( lookingForV4 ){ + retVal = true; + } + else { + throw new AAIException("AAI_6120", "Bad data. Address is a V4, but addressType said it should be V6. [" + + addrVal + "], [" + addrVer + "] passed to ipAddressFormatOK()"); + } + } + else if( inetAddr instanceof Inet6Address ){ + if( lookingForV6 ){ + retVal = true; + } + else { + throw new AAIException("AAI_6120", "Bad data. Address is a V6, but addressType said it should be V4. [" + + addrVal + "], [" + addrVer + "] passed to ipAddressFormatOK()."); + } + } + } + catch (IllegalArgumentException e) { + throw new AAIException("AAI_6120", "Badly formed ip-address: [" + addrVal + "] passed to ipAddressFormatOK()"); + } + + return retVal; + + }//end of ipAddressFormatOk() + + /** + * Ip address format OK. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param addrVal the addr val + * @param addrVer the addr ver + * @return the boolean + * @throws AAIException the AAI exception + */ + public static Boolean ipAddressFormatOK(String transId, String fromAppId, String addrVal, String addrVer) throws AAIException{ + return ipAddressFormatOK( transId, fromAppId, addrVal, addrVer, null); + } + + /** + * Save aai edge to db. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param edgeLabel the edge label + * @param outV the out V + * @param inV the in V + * @param propHash the prop hash + * @param apiVersion the api version + * @return TitanEdge + * @throws AAIException the AAI exception + */ + private static TitanEdge saveAaiEdgeToDb(String transId, String fromAppId, TitanTransaction graph, String edgeLabel, + TitanVertex outV, TitanVertex inV, HashMap <String,Object> propHash, String apiVersion) throws AAIException{ + + // If this edge doesn't exist yet, then create it. + + // NOTE - the Titan javaDoc says that there might not always be an id for a node. + // This is the internal-titan-unique-id, not any of our data. + // Not sure how to know when it might be there and when it might not?! + // So far, it has worked for all my testing, but this might warrant some + // further investigation. + + TitanEdge existingEdge = null; + String inVId = inV.id().toString(); + Iterator <Edge> eI = outV.edges(Direction.BOTH, edgeLabel); + while( eI.hasNext() ){ + TitanEdge ed = (TitanEdge) eI.next(); + TitanVertex otherVtx = (TitanVertex) ed.otherVertex(outV); + if( (otherVtx.id().toString()).equals(inVId) ){ + // NOTE -?- Not sure -- at some point we might want to check the edgeLabels also since we might + // want to allow two different-type edges between the same two vertexes? (or maybe not.) + existingEdge = ed; + break; + } + } + + if( existingEdge != null ){ + // This is just an UPDATE + for( Map.Entry<String, Object> entry : propHash.entrySet() ){ + LOGGER.debug("update edge property/val = [" + entry.getKey() + "]/[" + entry.getValue() + "]"); + existingEdge.property( entry.getKey(), entry.getValue() ); + } + + return( existingEdge ); + } + else { + // This is an ADD + + // Uniqueness double-check. This is just to catch the possibility that at the transaction layer, + // if data came in for two identical nodes that point to the same dependant node (for uniqueness), + // we would only be able to catch the problem at the time the edge to the second node is added. + // For example - if they had a VM and then got a request to add two ipAddress nodes, but some + // bad data was passed and those two ipAddress nodes were identical -- we'd want to catch it. + String outV_NType = outV.<String>property("aai-node-type").orElse(null); + String inV_NType = inV.<String>property("aai-node-type").orElse(null); + if( needsADepNode4Uniqueness(transId, fromAppId, outV_NType, apiVersion) + && nodeTypeACanDependOnB(transId, fromAppId, outV_NType, inV_NType, apiVersion) ){ + // The out-Vertex has a uniqueness dependency on the in-vertex + // Make sure we haven't already added an node/edge like this in this transaction + HashMap <String, Object> nodeKeyPropsHash = getNodeKeyPropHash(transId, fromAppId, graph, outV); + ArrayList<TitanVertex> resultList = new ArrayList<TitanVertex>(); + resultList = DbMeth.getConnectedNodes("transId", "fromAppId", graph, outV_NType, nodeKeyPropsHash, inV, apiVersion, false); + if( resultList.size() > 0 ){ + String propInfo = ""; + if( nodeKeyPropsHash != null ){ + propInfo = nodeKeyPropsHash.toString(); + } + throw new AAIException("AAI_6117", "Failed to add edge. This node (" + inV_NType + ") already has an edge to a " + outV_NType + + " node with kepProps [" + propInfo + "]"); + } + } + else if( needsADepNode4Uniqueness(transId, fromAppId, inV_NType, apiVersion) + && nodeTypeACanDependOnB(transId, fromAppId, inV_NType, outV_NType, apiVersion) ){ + // The in-Vertex has a uniqueness dependency on the out-vertex + // Make sure we haven't already added an node/edge like this in this transaction + HashMap <String, Object> nodeKeyPropsHash = getNodeKeyPropHash(transId, fromAppId, graph, inV); + ArrayList<TitanVertex> resultList = new ArrayList<TitanVertex>(); + resultList = DbMeth.getConnectedNodes("transId", "fromAppId", graph, inV_NType, nodeKeyPropsHash, outV, apiVersion, false); + if( resultList.size() > 0 ){ + String propInfo = ""; + if( nodeKeyPropsHash != null ){ + propInfo = nodeKeyPropsHash.toString(); + } + throw new AAIException("AAI_6117", "Failed to add edge. This node (" + outV_NType + ") already has an edge to a " + inV_NType + + " node with kepProps [" + propInfo + "]"); + } + } + + + // We're good to go to add this edge + + TitanEdge tEdge = outV.addEdge( edgeLabel, inV ); + // Add the properties to the new Edge + for( Map.Entry<String, Object> entry : propHash.entrySet() ){ + tEdge.property( entry.getKey(), entry.getValue() ); + } + + // For (resource-id updates) we need to "touch" the vertices on each side of the edge so + // anybody working on one of those vertices will know that something (ADDing this edge) has happened. + touchVertex( transId, fromAppId, inV ); + touchVertex( transId, fromAppId, outV ); + + return tEdge; + } + + }// End saveAaiEdgeToDb() + + + + /** + * Derive edge rule key for this edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param tEdge the t edge + * @return String - key to look up edgeRule (fromNodeType|toNodeType) + * @throws AAIException the AAI exception + */ + public static String deriveEdgeRuleKeyForThisEdge( String transId, String fromAppId, TitanTransaction graph, + TitanEdge tEdge ) throws AAIException{ + + TitanVertex fromVtx = tEdge.outVertex(); + TitanVertex toVtx = tEdge.inVertex(); + String startNodeType = fromVtx.<String>property("aai-node-type").orElse(null); + String targetNodeType = toVtx.<String>property("aai-node-type").orElse(null); + String key = startNodeType + "|" + targetNodeType; + if( EdgeRules.getInstance().hasEdgeRule(startNodeType, targetNodeType) ){ + // We can use the node info in the order they were given + return( key ); + } + else { + key = targetNodeType + "|" + startNodeType; + if( EdgeRules.getInstance().hasEdgeRule(targetNodeType, startNodeType) ){ + return( key ); + } + else { + // Couldn't find a rule for this edge + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + startNodeType + ", " + + targetNodeType); + } + } + }// end of deriveEdgeRuleKeyForThisEdge() + + + + /** + * Save aai edge to db. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param edgeLabel the edge label + * @param outV the out V + * @param inV the in V + * @param propHash the prop hash + * @return the titan edge + * @throws AAIException the AAI exception + */ + @Deprecated + private static TitanEdge saveAaiEdgeToDb(String transId, String fromAppId, TitanTransaction graph, String edgeLabel, + TitanVertex outV, TitanVertex inV, HashMap <String,Object> propHash) throws AAIException{ + return saveAaiEdgeToDb( transId, fromAppId, graph, edgeLabel, + outV, inV, propHash, null); + } + + /** + * Persist aai edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetVert the target vert + * @param apiVersion the api version + * @return the titan edge + * @throws AAIException the AAI exception + */ + public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, TitanVertex targetVert, String apiVersion ) throws AAIException{ + TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion, ""); + return returnEdge; + } + + /** + * Persist aai edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetVert the target vert + * @param apiVersion the api version + * @param edgeType the edge type + * @return TitanEdge + * @throws AAIException the AAI exception + */ + public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, TitanVertex targetVert, String apiVersion, String edgeType ) throws AAIException{ + + TitanVertex fromVtx = null; + TitanVertex toVtx = null; + String startNodeType = startVert.<String>property("aai-node-type").orElse(null); + String targetNodeType = targetVert.<String>property("aai-node-type").orElse(null); + String fwdRuleKey = startNodeType + "|" + targetNodeType; + int fwdRuleCount = 0; + String fwdRule = ""; + String fwdLabel = ""; + String revRuleKey = targetNodeType + "|" + startNodeType; + int revRuleCount = 0; + String revRule = ""; + String revLabel = ""; + String edRule = ""; + String edLabel = ""; + + Boolean checkType = false; + if( (edgeType != null) && edgeType != "" ){ + checkType = true; + } + + // As of 16-07, it is possible to have more than one kind of edge defined between a given + // pair of nodeTypes. So we need to check to see if there is only one possibility, or if + // we need to look at the edgeType to determine which to use. + // NOTE -- we're only supporting having 2 edges between a given pair of nodeTypes and + // one and only one of them would have to be a parent-child edge. + + if( DbEdgeRules.EdgeRules.containsKey(fwdRuleKey) ){ + Collection <String> edRuleColl = DbEdgeRules.EdgeRules.get(fwdRuleKey); + Iterator <String> ruleItr = edRuleColl.iterator(); + while( ruleItr.hasNext() ){ + String tmpRule = ruleItr.next(); + String [] rules = tmpRule.split(","); + String tmpLabel = rules[0]; + String tmpParChild = rules[3]; + if( !checkType + || (checkType && tmpParChild.equals("true") && edgeType.equals("parentChild")) + || (checkType && tmpParChild.equals("false") && edgeType.equals("cousin")) ){ + // Either they didn't want us to check the edgeType or it is a match + fwdRuleCount++; + if( fwdRuleCount > 1 ){ + // We found more than one with the given info + throw new AAIException("AAI_6120", "Multiple EdgeRules found for nodeTypes: [" + startNodeType + "], [" + + targetNodeType + "], edgeType = [" + edgeType + "]."); + } + else { + fwdRule = tmpRule; + fwdLabel = tmpLabel; + } + } + } + } + + // Try it the other way also (unless this is the case of a nodeType recursively pointing to itself + // Ie. the edge rule: "model-element|model-element" + if( !revRuleKey.equals(fwdRuleKey) && DbEdgeRules.EdgeRules.containsKey(revRuleKey) ){ + Collection <String> edRuleColl = DbEdgeRules.EdgeRules.get(revRuleKey); + Iterator <String> ruleItr = edRuleColl.iterator(); + while( ruleItr.hasNext() ){ + String tmpRule = ruleItr.next(); + String [] rules = tmpRule.split(","); + String tmpLabel = rules[0]; + String tmpParChild = rules[3]; + if( !checkType + || (checkType && tmpParChild.equals("true") && edgeType.equals("parentChild")) + || (checkType && tmpParChild.equals("false") && edgeType.equals("cousin")) ){ + // Either they didn't want us to check the edgeType or it is a match + revRuleCount++; + if( revRuleCount > 1 ){ + // We found more than one with the given info + throw new AAIException("AAI_6120", "Multiple EdgeRules found for nodeTypes: [" + targetNodeType + "], [" + + startNodeType + "], edgeType = [" + edgeType + "]."); + } + else { + revRule = tmpRule; + revLabel = tmpLabel; + } + } + } + } + + if( (fwdRuleCount == 1) && (revRuleCount == 0) ){ + // We can use the node info in the order they were given + fromVtx = startVert; + toVtx = targetVert; + edRule = fwdRule; + edLabel = fwdLabel; + } + else if( (fwdRuleCount == 0) && (revRuleCount == 1) ){ + // We need to switch the vertex order so the edge-direction is correct + toVtx = startVert; + fromVtx = targetVert; + edRule = revRule; + edLabel = revLabel; + } + else if( (fwdRuleCount == 0) && (revRuleCount == 0) ){ + // No edge rule found for this + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + startNodeType + ", " + targetNodeType + + "], checkLabelType = [" + edgeType + "]."); + } + else if( (fwdRuleCount > 0) && (revRuleCount > 0) ){ + // We found more than one with the given info + throw new AAIException("AAI_6120", "Multiple EdgeRules (fwd and rev) found for nodeTypes: [" + startNodeType + "], [" + + targetNodeType + "], checkLabelType = [" + edgeType + "]."); + } + + // If we got to this point, we now have a single edge label and we know to and from Vtx. + + HashMap <String,Object> edgeParamHash = getEdgeTagPropPutHash4Rule(transId, fromAppId, edRule); + // We do "source-of-truth" for all edges + edgeParamHash.put("source-of-truth", fromAppId ); + + TitanEdge returnEdge = saveAaiEdgeToDb(transId, fromAppId, graph, edLabel, fromVtx, toVtx, edgeParamHash, apiVersion); + + return returnEdge; + + } + + /** + * Persist aai edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetVert the target vert + * @return the titan edge + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, TitanVertex targetVert ) throws AAIException{ + return persistAaiEdge( transId, fromAppId, graph, + startVert, targetVert, null); + } + // End persistAaiEdge() + + + /** + * Persist aai edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param edgeLabel the edge label + * @param startVert the start vert + * @param targetVert the target vert + * @param propHash the prop hash + * @param addIfNotFound the add if not found + * @return the titan edge + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph, + String edgeLabel, TitanVertex startVert, TitanVertex targetVert, + HashMap <String,Object> propHash, Boolean addIfNotFound ) throws AAIException{ + + /*----- This method is depricated ------ + * We will ignore the parameters: edgeLabel, propHash and addIfNotFound + * We will use the remaining params to call the newer version of this method + */ + TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, null); + + return returnEdge; + + }// End depricated version of persistAaiEdge() + + + /** + * Persist aai edge with dep params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetNodeType the target node type + * @param targetNodeParamHash the target node param hash + * @param apiVersion the api version + * @return TitanEdge + * @throws AAIException the AAI exception + */ + public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash, String apiVersion) throws AAIException{ + + TitanVertex targetVert = getUniqueNodeWithDepParams( transId, fromAppId, graph, targetNodeType, targetNodeParamHash, apiVersion ); + TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion); + + return returnEdge; + + }// End persistAaiEdgeWithDepParams() + + // Version that lets you pass in an edgeType ("parentChild" or "cousin" since it sometimes cannot be determined + /** + * Persist aai edge with dep params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetNodeType the target node type + * @param targetNodeParamHash the target node param hash + * @param apiVersion the api version + * @param edgeType the edge type + * @return the titan edge + * @throws AAIException the AAI exception + */ + // from the two nodeTypes anymore (16-07) + public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash, + String apiVersion, String edgeType) throws AAIException{ + TitanVertex targetVert = getUniqueNodeWithDepParams( transId, fromAppId, graph, targetNodeType, targetNodeParamHash, apiVersion ); + TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion, edgeType); + + return returnEdge; + + }// End persistAaiEdgeWithDepParams() + + /** + * Persist aai edge with dep params. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param startVert the start vert + * @param targetNodeType the target node type + * @param targetNodeParamHash the target node param hash + * @return the titan edge + * @throws AAIException the AAI exception + */ + @Deprecated + public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph, + TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash) throws AAIException{ + return persistAaiEdgeWithDepParams( transId, fromAppId, graph, + startVert, targetNodeType, targetNodeParamHash, null); + } + + /** + * Gets the node key prop hash. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param vtx the vtx + * @return nodeKeyPropHash + * @throws AAIException the AAI exception + */ + public static HashMap <String, Object> getNodeKeyPropHash( String transId, String fromAppId, TitanTransaction graph, TitanVertex vtx) throws AAIException{ + + if( vtx == null ){ + throw new AAIException("AAI_6109", "null node object passed to getNodeKeyPropHash()."); + } + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + String nType = vtx.<String>property("aai-node-type").orElse(null); + if( ! dbMaps.NodeKeyProps.containsKey(nType) ){ + // Problem if no key Properties defined for this nodeType + String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + throw new AAIException("AAI_6105", "No node-key-properties defined in dbMaps for nodeType = " + nType + " (ver=" + defVer + ")"); + } + + HashMap <String,Object>nodeKeyPropsHash = new HashMap<String,Object>(); + Collection <String> keyProps = dbMaps.NodeKeyProps.get(nType); + Iterator <String> keyPropI = keyProps.iterator(); + while( keyPropI.hasNext() ){ + String propName = keyPropI.next(); + Object value = (Object) vtx.<Object>property(propName).orElse(null); + nodeKeyPropsHash.put(propName, value); + } + + return nodeKeyPropsHash; + + }// End of getNodeKeyPropHash() + + /** + * Gets the node name prop hash. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param vtx the vtx + * @param apiVersion the api version + * @return nodeKeyPropHash + * @throws AAIException the AAI exception + */ + public static HashMap <String, Object> getNodeNamePropHash( String transId, String fromAppId, TitanTransaction graph, TitanVertex vtx, String apiVersion) throws AAIException{ + + if( vtx == null ){ + throw new AAIException("AAI_6109", "null node object passed to getNodeNamePropHash()." ); + } + + String nType = vtx.<String>property("aai-node-type").orElse(null); + HashMap <String,Object>nodeNamePropsHash = new HashMap<String,Object>(); + Collection <String> keyProps = DbMeth.getNodeNameProps(transId, fromAppId, nType, apiVersion); + Iterator <String> keyPropI = keyProps.iterator(); + while( keyPropI.hasNext() ){ + String propName = keyPropI.next(); + Object value = (Object) vtx.<Object>property(propName).orElse(null); + nodeNamePropsHash.put(propName, value); + } + + return nodeNamePropsHash; + + }// End of getNodeNamePropHash() + + + /** + * Removes the aai edge. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param tEdge the t edge + * @return void + */ + public static void removeAaiEdge( String transId, String fromAppId, TitanTransaction graph, TitanEdge tEdge){ + // Before removing the edge, touch the vertices on each side so their resource-versions will get updated + TitanVertex tmpVIn = tEdge.inVertex(); + touchVertex( transId, fromAppId, tmpVIn ); + + TitanVertex tmpVOut = tEdge.outVertex(); + touchVertex( transId, fromAppId, tmpVOut ); + + // Remove the passed in edge. + tEdge.remove(); + + }// end of removeAaiEdge() + + + /** + * Removes the aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param thisVtx the this vtx + * @param scopeParam the scope param + * @param apiVersion the api version + * @param resourceVersion the resource version + * @throws AAIException the AAI exception + */ + public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, + String apiVersion, String resourceVersion ) throws AAIException{ + // Note: the resource Version Override flag is only set to true when called by the Model Delete code which + // has no way to know the resource-versions of nodes at lower-levels of it's model topology. + Boolean resVersionOverrideFlag = false; + removeAaiNode( transId, fromAppId, graph, thisVtx, scopeParam, apiVersion, resourceVersion, resVersionOverrideFlag ); + } + + + /** + * <pre> + * 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! + * </pre>. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param thisVtx the this vtx + * @param scopeParam the scope param + * @param apiVersion the api version + * @param resourceVersion the resource version + * @param resVerOverride the res ver override + * @return void + * @throws AAIException the AAI exception + */ + public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, + String apiVersion, String resourceVersion, Boolean resVerOverride ) throws AAIException{ + String nodeType2Del = thisVtx.<String>property("aai-node-type").orElse(null); + String deleteScope = scopeParam; + if( scopeParam.equals("USE_DEFAULT") ){ + deleteScope = getDefaultDeleteScope(transId, fromAppId, nodeType2Del, apiVersion); + } + + if( !resVerOverride && needToDoResourceVerCheck(apiVersion, false) ){ + // Need to check that they knew what they were deleting + String existingResVer = thisVtx.<String>property("resource-version").orElse(null); + if( resourceVersion == null || resourceVersion.equals("") ){ + throw new AAIException("AAI_6130", "Resource-version not passed for delete of = " + nodeType2Del); + } + else if( (existingResVer != null) && !resourceVersion.equals(existingResVer) ){ + throw new AAIException("AAI_6131", "Resource-version MISMATCH for delete of = " + nodeType2Del); + } + } + + if( !deleteScope.equals("THIS_NODE_ONLY") + && !deleteScope.equals("CASCADE_TO_CHILDREN") + && !deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE") + && !deleteScope.equals("ERROR_IF_ANY_EDGES") + && !deleteScope.equals("ERROR_IF_ANY_IN_EDGES") ){ + throw new AAIException("AAI_6120", "Unrecognized value in deleteScope: [" + deleteScope + "]."); + } + + if( deleteScope.equals("ERROR_IF_ANY_EDGES") ){ + if ( thisVtx.edges(Direction.BOTH).hasNext() ) { + throw new AAIException("AAI_6110", "Node cannot be deleted because it still has Edges and the ERROR_IF_ANY_EDGES scope was used."); + } + } + else if( deleteScope.equals("ERROR_IF_ANY_IN_EDGES") || deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE") ){ + Iterator <Edge> eI = thisVtx.edges(Direction.IN); + boolean onlyHasParent = false; + Edge temp = null; + if( eI != null && eI.hasNext() ){ + temp = eI.next(); + Boolean isParent = temp.<Boolean>property("isParent").orElse(null); + if (isParent != null && isParent && !eI.hasNext()) { + onlyHasParent = true; + } + + if (!onlyHasParent) { + throw new AAIException("AAI_6110", "Node cannot be deleted because it still has Edges and the " + deleteScope + " scope was used."); + } + } + } + else if( deleteScope.equals("THIS_NODE_ONLY")){ + // Make sure nobody depends on this node. + Iterator<Edge> eI = thisVtx.edges(Direction.BOTH); + while( eI.hasNext() ){ + TitanEdge ed = (TitanEdge) eI.next(); + TitanVertex otherVtx = (TitanVertex) ed.otherVertex(thisVtx); + String nodeTypeA = otherVtx.<String>property("aai-node-type").orElse(null); + if( nodeTypeACanDependOnB(transId, fromAppId, nodeTypeA, nodeType2Del, apiVersion)){ + // We're only supposed to delete this node - but another node is dependant on it, + // so we shouldn't delete this one. + throw new AAIException("AAI_6110", "Node cannot be deleted using scope = " + deleteScope + + " another node (type = " + nodeTypeA + ") depends on it for uniqueness."); + } + } + } + + // We've passed our checks - so do some deleting of edges and maybe pass + // the delete request down to children or delete-targets. + + // First we deal with the "IN"-Edges which can't have children/delete-targets which + // by definition (of "IN") on the other end + Iterator <Edge> eI_In = thisVtx.edges(Direction.IN); + while( eI_In.hasNext() ){ + TitanEdge ed = (TitanEdge) eI_In.next(); + + //- "touch" vertex on other side of this edge so it gets a fresh resource-version + TitanVertex tmpVOther = ed.otherVertex(thisVtx); + touchVertex( transId, fromAppId, tmpVOther ); + + ed.remove(); + } + + // Now look at the "OUT"-edges which might include children or delete-targets + String cascadeMsg = "This nt = " + nodeType2Del + ", Cascading del to: "; + Iterator <Edge> eI_Out = thisVtx.edges(Direction.OUT); + if( !eI_Out.hasNext() ){ + cascadeMsg = cascadeMsg + "[no children for this node]"; + } + while( eI_Out.hasNext() ){ + TitanEdge ed = (TitanEdge) eI_Out.next(); + + // "touch" vertex on other side of this edge so it gets a fresh resource-version + TitanVertex tmpVOther = ed.otherVertex(thisVtx); + touchVertex( transId, fromAppId, tmpVOther ); + + Boolean otherVtxAChild = ed.<Boolean>property("isParent").orElse(null); + if( otherVtxAChild == null ){ + otherVtxAChild = false; + } + + Boolean otherVtxADeleteTarget = ed.<Boolean>property("hasDelTarget").orElse(null); + if( otherVtxADeleteTarget == null ){ + otherVtxADeleteTarget = false; + } + + if( (otherVtxAChild || otherVtxADeleteTarget) && + (deleteScope.equals("CASCADE_TO_CHILDREN") || deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE")) ){ + // Delete the edge to the child and Pass the delete down to it. + ed.remove(); + TitanVertex otherVtx = (TitanVertex) ed.otherVertex(thisVtx); + String vid = otherVtx.id().toString(); + String nty = otherVtx.<String>property("aai-node-type").orElse(null); + String resVers = otherVtx.<String>property("resource-version").orElse(null); + cascadeMsg = cascadeMsg + "[" + nty + ":" + vid + "]"; + removeAaiNode(transId, fromAppId, graph, otherVtx, "CASCADE_TO_CHILDREN", apiVersion, resVers); + } + else { + // The other node is not a child or deleteTarget. Delete the edge to it if it is not + // dependent (Should never be dependent since it's not a child/delTarget... but + // someone could create a node that was dependent for Uniqueness without + // being a child/target. + + // DEBUG -- eventually add the check for dependancy that isn't on a parent-type or delTarget-type edge + ed.remove(); + } + } + + LOGGER.info(cascadeMsg); + + Iterator<Edge> eI = thisVtx.edges(Direction.BOTH); + if( ! eI.hasNext() ){ + // By this point, either there were no edges to deal with, or we have dealt with them. + thisVtx.remove(); + } + else { + // Something went wrong and we couldn't delete all the edges for this guy. + throw new AAIException("AAI_6110", "Node could be deleted because it unexpectedly still has Edges.\n"); + } + } + + + /** + * Removes the aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param thisVtx the this vtx + * @param scopeParam the scope param + * @return void + * @throws AAIException the AAI exception + */ + @Deprecated + public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam) throws AAIException{ + removeAaiNode(transId, fromAppId, graph, thisVtx, scopeParam, null, null); + } + + /** + * Removes the aai node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param thisVtx the this vtx + * @param scopeParam the scope param + * @param apiVersion the api version + * @throws AAIException the AAI exception + */ + @Deprecated + public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, + String apiVersion ) throws AAIException{ + removeAaiNode(transId, fromAppId, graph, thisVtx, scopeParam, apiVersion, null); + } + // end of removeAaiNode() + + + /** + * Delete all graph data. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @return void + */ + public static void deleteAllGraphData( String transId, String fromAppId, TitanGraph graph ){ + /** ====================================================================== + * WARNING -- this removes ALL the data that is currently in the graph. + * ====================================================================== + **/ + LOGGER.warn("deleteAllGraphData called! Run for the hills!"); + Iterator<Edge> edges = graph.edges(Direction.BOTH); + graph.tx().commit(); + Edge edge = null; + while (edges.hasNext()) { + edge = edges.next(); + edges.remove(); + } + graph.tx().commit(); + Iterator<Vertex> vertices = graph.vertices(); + graph.tx().commit(); + Vertex vertex = null; + while (vertices.hasNext()) { + vertex = vertices.next(); + vertex.remove(); + } + graph.tx().commit(); + } + + + /** + * Show all edges for node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param tVert the t vert + * @return the array list + */ + public static ArrayList <String> showAllEdgesForNode( String transId, String fromAppId, TitanVertex tVert ){ + + ArrayList <String> retArr = new ArrayList <String> (); + Iterator <Edge> eI = tVert.edges(Direction.IN); + if( ! eI.hasNext() ){ + retArr.add("No IN edges were found for this vertex. "); + } + while( eI.hasNext() ){ + TitanEdge ed = (TitanEdge) eI.next(); + String lab = ed.label(); + TitanVertex vtx = (TitanVertex) ed.otherVertex(tVert); + if( vtx == null ){ + retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< "); + } + else { + String nType = vtx.<String>property("aai-node-type").orElse(null); + String vid = vtx.id().toString(); + retArr.add("Found an IN edge (" + lab + ") to this vertex from a [" + nType + "] node with VtxId = " + vid ); + //DEBUG --- + //showPropertiesForEdge( transId, fromAppId, ed ); + } + } + + eI = tVert.edges(Direction.OUT); + if( ! eI.hasNext() ){ + retArr.add("No OUT edges were found for this vertex. "); + } + while( eI.hasNext() ){ + TitanEdge ed = (TitanEdge) eI.next(); + String lab = ed.label(); + TitanVertex vtx = (TitanVertex) ed.otherVertex(tVert); + if( vtx == null ){ + retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< "); + } + else { + String nType = vtx.<String>property("aai-node-type").orElse(null); + String vid = vtx.id().toString(); + retArr.add("Found an OUT edge (" + lab + ") from this vertex to a [" + nType + "] node with VtxId = " + vid ); + //DEBUG --- + //showPropertiesForEdge( transId, fromAppId, ed ); + } + } + return retArr; + } + + + /** + * Show properties for node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param tVert the t vert + * @return the array list + */ + public static ArrayList <String> showPropertiesForNode( String transId, String fromAppId, TitanVertex tVert ){ + + ArrayList <String> retArr = new ArrayList <String> (); + if( tVert == null ){ + retArr.add("null Node object passed to showPropertiesForNode()\n"); + } + else { + String nodeType = ""; + //String datType = ""; + Object ob = tVert.<Object>property("aai-node-type").orElse(null); + if( ob == null ){ + nodeType = "null"; + } + else{ + nodeType = ob.toString(); + //datType = ob.getClass().getSimpleName(); + } + + retArr.add(" AAINodeType/VtxID for this Node = [" + nodeType + "/" + tVert.id() + "]"); + retArr.add(" Property Detail: "); + Iterator<VertexProperty<Object>> pI = tVert.properties(); + while( pI.hasNext() ){ + VertexProperty<Object> tp = pI.next(); + Object val = tp.value(); + //retArr.add("Prop: [" + tp.getPropertyKey() + "], val = [" + val + "], dataType = " + val.getClass() ); + retArr.add("Prop: [" + tp.key() + "], val = [" + val + "] "); + } + } + return retArr; + } + + + /** + * Gets the node name props. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the node type + * @param apiVersion the api version + * @return HashMap of keyProperties + * @throws AAIException the AAI exception + */ + public static Collection <String> getNodeNameProps( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + Collection <String> nameProps = new ArrayList <String>(); + if( dbMaps.NodeNameProps.containsKey(nodeType) ){ + nameProps = dbMaps.NodeNameProps.get(nodeType); + } + else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){ + // The passed-in nodeType was really a nodeCategory, theoretically, all the guys in the same + // category should have the same name property -- so if they just give us the category, we will + // just give the name info from the first nodeType we encounter of that category. + Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType); + Iterator <String> catItr = nTypeCatCol.iterator(); + String catInfo = ""; + if( catItr.hasNext() ){ + // For now, we only look for one. + catInfo = catItr.next(); + } + else { + throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType); + } + + String [] flds = catInfo.split(","); + if( flds.length != 4 ){ + throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); + } + + String nodeTypesString = flds[0]; + String [] nodeTypeNames = nodeTypesString.split("\\|"); + if( nodeTypeNames != null && nodeTypeNames.length > 0 ){ + // We'll just use the first one + String nt = nodeTypeNames[0]; + nameProps = dbMaps.NodeNameProps.get(nt); + } + } + + + // Note - it's ok if there was no defined name property for this nodeType. + + return nameProps; + + }// end of getNodeKeyPropNames + + + /** + * Gets the edge tag prop put hash 4 rule. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param edRule the ed rule + * @return the edge tag prop put hash 4 rule + * @throws AAIException the AAI exception + */ + public static HashMap <String,Object> getEdgeTagPropPutHash4Rule( String transId, String fromAppId, String edRule ) + throws AAIException{ + // For a given edgeRule - already pulled out of DbEdgeRules.EdgeRules -- parse out the "tags" that + // need to be set for this kind of edge. + // These are the Boolean properties like, "isParent", "usesResource" etc. + HashMap <String,Object> retEdgePropPutMap = new HashMap <String,Object>(); + + if( (edRule == null) || edRule.equals("") ){ + // No edge rule found for this + throw new AAIException("AAI_6120", "blank edRule passed to getEdgeTagPropPutHash4Rule()"); + } + + int tagCount = DbEdgeRules.EdgeInfoMap.size(); + String [] rules = edRule.split(","); + if( rules.length != tagCount ){ + throw new AAIException("AAI_6121", "Bad EdgeRule data (itemCount =" + rules.length + ") for rule = [" + edRule + "]."); + } + + // In DbEdgeRules.EdgeRules -- What we have as "edRule" 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 + for( int i = DbEdgeRules.firstTagIndex; i < tagCount; i++ ){ + String booleanStr = rules[i]; + Integer mapKey = new Integer(i); + String propName = DbEdgeRules.EdgeInfoMap.get(mapKey); + String revPropName = propName + "-REV"; + + if( booleanStr.equals("true") ){ + retEdgePropPutMap.put(propName, true); + retEdgePropPutMap.put(revPropName,false); + } + else if( booleanStr.equals("false") ){ + retEdgePropPutMap.put(propName, false); + retEdgePropPutMap.put(revPropName,false); + } + else if( booleanStr.equals("reverse") ){ + retEdgePropPutMap.put(propName, false); + retEdgePropPutMap.put(revPropName,true); + } + else { + throw new AAIException("AAI_6121", "Bad EdgeRule data for rule = [" + edRule + "], val = [" + booleanStr + "]."); + } + + } + + return retEdgePropPutMap; + + } // End of getEdgeTagPropPutHash() + + + + /** + * Gets the edge tag prop put hash. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param edgeRuleKey the edge rule key + * @return the edge tag prop put hash + * @throws AAIException the AAI exception + */ + public static Map<String, EdgeRule> getEdgeTagPropPutHash( String transId, String fromAppId, String edgeRuleKey ) + throws AAIException{ + // For a given edgeRuleKey (nodeTypeA|nodeTypeB), look up the rule that goes with it in + // DbEdgeRules.EdgeRules and parse out the "tags" that need to be set on each edge. + // These are the Boolean properties like, "isParent", "usesResource" etc. + // Note - this code is also used by the updateEdgeTags.java code + + String[] edgeRuleKeys = edgeRuleKey.split("\\|"); + + if (edgeRuleKeys.length < 2 || ! EdgeRules.getInstance().hasEdgeRule(edgeRuleKeys[0], edgeRuleKeys[1])) { + throw new AAIException("AAI_6120", "Could not find an DbEdgeRule entry for passed edgeRuleKey (nodeTypeA|nodeTypeB): " + edgeRuleKey + "."); + } + + Map<String, EdgeRule> edgeRules = EdgeRules.getInstance().getEdgeRules(edgeRuleKeys[0], edgeRuleKeys[1]); + + return edgeRules; + + } // End of getEdgeTagPropPutHash() + + + /** + * This property was put by newer version of code. + * + * @param apiVersionStr the api version str + * @param nodeType the node type + * @param propName the prop name + * @return true, if successful + * @throws AAIException the AAI exception + */ + private static boolean thisPropertyWasPutByNewerVersionOfCode( String apiVersionStr, + String nodeType, String propName) throws AAIException{ + // We want to return True if the nodeType + property-name combo was introduced AFTER the apiVersion passed. + + int apiVerInt = 0; + int propIntroVerInt = 0; + + if( apiVersionStr == null || apiVersionStr.equals("") ){ + apiVersionStr = org.openecomp.aai.util.AAIApiVersion.get(); + } + apiVerInt = getVerNumFromVerString(apiVersionStr); + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + String propIntroKey = nodeType + "|" + propName; + if( propName.equals("prov-status") ){ + // This is a special case -- The dbMaps from v2 has it in there, but it was introduced half way through. So + // it needs to be catogorized as v3. + propIntroVerInt = 3; + } + else if( ! dbMaps.PropertyVersionInfoMap.containsKey(propIntroKey) ){ + String detail = propIntroKey + " [" + propIntroKey + "] not found in dbMaps.PropertyVersionInfoMap."; + throw new AAIException("AAI_6121", detail); + } + else { + String propIntroVerString = dbMaps.PropertyVersionInfoMap.get(propIntroKey); + propIntroVerInt = getVerNumFromVerString( propIntroVerString ); + } + + if( propIntroVerInt > apiVerInt ){ + return true; + } + else { + return false; + } + + } // End of thisPropertyWasPutByNewerVersionOfCode() + + + /** + * Touch vertex. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param v the v + * @return void + */ + public static void touchVertex( String transId, String fromAppId, TitanVertex v ){ + // We want to "touch" the vertex -- Ie. update it's last-mod-date, last-mod- resource-version to the current date/time + if( v != null ){ + long unixTimeNow = System.currentTimeMillis() / 1000L; + String timeNowInSec = "" + unixTimeNow; + v.property( "aai-last-mod-ts", timeNowInSec ); + v.property( "resource-version", timeNowInSec ); + v.property( "last-mod-source-of-truth", fromAppId ); + } + } // End of touchVertex() + + + /** + * Check prop cardinality. + * + * @param propName the prop name + * @param cardinalityType the cardinality type + * @return boolean + * @throws AAIException the AAI exception + */ + public static boolean checkPropCardinality( String propName, String cardinalityType ) throws AAIException { + + // Return true if the named property is tagged in our dbMaps PropetyDataTypeMap as + // having the passed in cardinality type. + // NOTE: supported cardinality types in dbMaps = "Set" or "List" + // In Titan (and ex5.json), those go in as "SET" and "LIST" + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + if( dbMaps.PropertyDataTypeMap.containsKey(propName) ){ + String propDataType = dbMaps.PropertyDataTypeMap.get(propName); + if( propDataType != null && propDataType.startsWith(cardinalityType) ){ + return true; + } + } + return false; + + } // End of checkPropCardinality() + + /** + * Convert type if needed. + * + * @param propName the prop name + * @param val the val + * @return convertedValue (if it was a String but needed to be a Boolean) + * @throws AAIException the AAI exception + */ + public static Object convertTypeIfNeeded( String propName, Object val ) + throws AAIException { + // Make sure the dataType of the passed-in Object matches what the DB expects + + // NOTE: since this is a fix very late in our dev cycle, we'll just fix the scenarios that + // we're having trouble with which is Strings getting into the db which should be going in as + // Booleans or Integers. + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + if( dbMaps.PropertyDataTypeMap.containsKey(propName) ){ + String dbExpectedDataType = dbMaps.PropertyDataTypeMap.get(propName); + if( dbExpectedDataType != null + && dbExpectedDataType.equals("Boolean") + && val != null + && !(val instanceof Boolean) ){ + String valStr = val.toString().trim(); + if( valStr.equals("true") || valStr.equals("True") || valStr.equals("TRUE") ){ + return Boolean.valueOf("true"); + } + else if( valStr.equals("false") || valStr.equals("False") || valStr.equals("FALSE") ){ + return Boolean.valueOf("false"); + } + else { + String emsg = "Error trying to convert value: [" + valStr + "] to a Boolean for property + " + propName + "\n"; + throw new AAIException("AAI_6120", emsg); + } + } + else if( dbExpectedDataType != null + && dbExpectedDataType.equals("Integer") + && val != null + && !(val.toString().trim().equals("")) + && !(val instanceof Integer) ){ + String valStr = val.toString().trim(); + Integer newInt; + try { + newInt = Integer.valueOf(valStr); + return newInt; + } + catch( Exception e ){ + String emsg = "Error trying to convert value: [" + valStr + "] to an Integer for property + " + propName + "\n"; + throw new AAIException("AAI_6120", emsg); + } + } + } + + // If it didn't need to be converted, just return it. + return val; + + } // End of convertTypeIfNeeded() + + + + /** + * This vertex not reachable. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param v the v + * @param version the version + * @return boolean + */ + public static boolean thisVertexNotReachable( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version){ + if( v == null ){ + return true; + } + else { + try { + v.id().toString(); + } + catch( Exception ex ){ + // Could not get this -- sometimes we're holding a vertex object that has gotten deleted, so + // when we try to get stuff from it, we get an "Element Has Been Removed" error from Titan + return true; + } + } + + return false; + + } // End of thisVertexNotReachable() + + /** + * This vertex has bad edges. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param v the v + * @param version the version + * @return boolean + */ + public static boolean thisVertexHasBadEdges( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version){ + + Iterator <Edge> eItor = v.edges(Direction.BOTH); + while( eItor.hasNext() ){ + Edge e = null; + e = eItor.next(); + if( e == null ){ + return true; + } + Vertex vIn = e.inVertex(); + if( (vIn == null) || (vIn.<String>property("aai-node-type").orElse(null) == null) ){ + // this is a bad edge because it points to a vertex that isn't there anymore + return true; + } + + Vertex vOut = e.outVertex(); + if( (vOut == null) || (vOut.<String>property("aai-node-type").orElse(null) == null) ){ + // this is a bad edge because it points to a vertex that isn't there anymore + return true; + } + } + + // If we made it to here, the vertex's edges must be ok. + return false; + + } // End of thisVertexHasBadEdges() + + + /** + * This vertex is A phantom. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param v the v + * @param version the version + * @return boolean + * @throws AAIException the AAI exception + */ + public static boolean thisVertexIsAPhantom( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version ) + throws AAIException { + + + // The kind of Phantom we're looking for is the kind that we sometimes get when we do a select without + // using key properties. They can be in the database as a vertex, but the indexes that should point to + // them are not working -- so they cannot be used by normal interfaces (like the REST API) which means + // that if we return it, it can mess up a caller who tries to use it. + if( v == null ){ + return true; + } + String thisVid = v.id().toString(); + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + Object propOb = v.<Object>property("aai-node-type").orElse(null); + if( propOb == null ){ + // This vertex does not have an aai-node-type ---> it is messed up + return true; + } + String nType = propOb.toString(); + if( ! dbMaps.NodeKeyProps.containsKey(nType) ){ + // This node Type does not have keys defined + // This could just be bad reference data, so we will not flag this guy, but we + // can't really do our test... + return false; + } + + HashMap <String,Object> propHashWithKeys = new HashMap<String, Object>(); + Collection <String> keyProps = null; + try { + keyProps = getNodeKeyPropNames(transId, fromAppId, nType, version); + } + catch (AAIException ex) { + // something wrong with getting this guy's key property names - we'll abandon this test... + return false; + } + + Iterator <String> keyPropI = keyProps.iterator(); + while( keyPropI.hasNext() ){ + String propName = keyPropI.next(); + String propVal = ""; + Object ob = v.<Object>property(propName).orElse(null); + if( ob != null ){ + propVal = ob.toString(); + } + propHashWithKeys.put(propName, propVal); + } + try { + // Note - We can get more than one back since some nodes need a dep. node for uniqueness. + // We don't care about that -- we just want to make sure we can get this vertex back when + // we're searching with it's indexed fields. + // NOTE - we're passing the skipGroomCheck to getNodes so we don't wind up in an infinite loop + ArrayList <TitanVertex> vertList2 = getNodes( transId, fromAppId, graph, nType, propHashWithKeys, false, version, true ); + Iterator<TitanVertex> iter2 = vertList2.iterator(); + while( iter2.hasNext() ){ + TitanVertex tvx2 = iter2.next(); + String foundId = tvx2.id().toString(); + if( foundId.equals( thisVid ) ){ + // We could get back the vertex by looking it up using key properties... That's good. + return false; + } + } + } + catch (Exception e2) { + //String msg = " Error encountered for this vertex id: [" + thisVid + + // "]. Caught this exception: " + e2.toString(); + // Something messed up - but that doesn't prove that this is a phantom. + return false; + } + + // If we dropped down to here, we have looked but could not pull the vertex out of the + // db using it's key fields, so it gets flagged as a Phantom. + return true; + + } // End of thisVertexIsAPhantom() + + + /** + * Gets the node by unique key. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param graph the graph + * @param aaiUniquekey the aai uniquekey + * @return the node by unique key + */ + public TitanVertex getNodeByUniqueKey(String transId, String fromAppId, TitanTransaction graph, String aaiUniquekey) { + + TitanVertex vert = null; + + Iterator<?> vertI = graph.query().has("aai-unique-key", aaiUniquekey).vertices().iterator(); + + if( vertI != null && vertI.hasNext()) { + // We found a vertex that meets the input criteria. + vert = (TitanVertex) vertI.next(); + } + + return vert; + } + + + +} + diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ModelBasedProcessing.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ModelBasedProcessing.java new file mode 100644 index 0000000..f51e67b --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ModelBasedProcessing.java @@ -0,0 +1,3718 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphgen; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +import org.openecomp.aai.db.DbMethHelper; +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.dbgen.PropertyLimitDesc; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.ingestModel.DbMaps; +import org.openecomp.aai.ingestModel.IngestModelMoxyOxm; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.db.EdgeRules; +import org.openecomp.aai.serialization.db.EdgeType; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +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.google.common.util.concurrent.SimpleTimeLimiter; +import com.google.common.util.concurrent.TimeLimiter; +import com.google.common.util.concurrent.UncheckedTimeoutException; +import com.thinkaurelius.titan.core.TitanVertex; + +/** + * Utility class that uses Model/Named-Query definitions to navigate the graph. + */ +public class ModelBasedProcessing{ + + private EELFLogger LOGGER = EELFManager.getInstance().getLogger(ModelBasedProcessing.class); + private final int MAX_LEVELS = 50; // max depth allowed for our model - to protect against infinite loop problems + + private TransactionalGraphEngine engine; + private Loader loader; + private DBSerializer serializer; + private DbMethHelper dbMethHelper; + + protected ModelBasedProcessing() { + + } + public ModelBasedProcessing(Loader loader, TransactionalGraphEngine engine, DBSerializer serializer) { + this.loader = loader; + this.engine = engine; + this.serializer = serializer; + dbMethHelper = new DbMethHelper(loader, engine); + } + /** + * Gets the start nodes and model-ver's. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param passedModelVersionId the passed model-version-id -- optional (unique id for a model-ver) + * @param passedModelId the passed model-invariant-id -- optional + * @param passedModelName the passed model-name -- optional + * @param passedTopNodeType the passed top node type -- optional (needed if neither model=invariant-id nor model-version-id is passed) + * @param startNodeFilterArrayOfHashes the start node filter array of hashes -- optional (used to locate the first node(s) of instance data) + * @param apiVer the api ver + * @return HashMap of startNodes and their corresponding model-version-id's + * @throws AAIException the AAI exception + */ + public Map<String,String> getStartNodesAndModVersionIds( String transId, String fromAppId, + String passedModelVersionId, + String passedModelInvId, + String passedModelName, + String passedTopNodeType, + List<Map<String,Object>> startNodeFilterArrayOfHashes, + String apiVer ) + throws AAIException{ + // ---------------------------------------------------------------------------------------------------- + // Get a hash for all start-nodes (key = vtxId, val = modelVersionId that applies) + // If no start-node-key info is passed, then use either the passed modelVersion or + // the passed model-invariant-id or model-name to collect them. + // If start-node-key info is given, use it instead to look for start-nodes. + // Note: if ONLY start-node-key info is given, then it would have to map to nodes which + // have persona data. Otherwise we'd have no way to know what model to collect data with. + // ---------------------------------------------------------------------------------------------------- + + Iterator<Vertex> startVerts = null; + Map<String, String> startVertInfo = new HashMap<>(); + + if( startNodeFilterArrayOfHashes.isEmpty() ){ + // Since they did not give any data to find start instances, we will have to find them + // using whatever model-info they provided so we can use it to map to persona-data in the db. + if( (passedModelVersionId == null || passedModelVersionId.equals("")) + && (passedModelInvId == null || passedModelInvId.equals("")) + && (passedModelName == null || passedModelName.equals(""))){ + throw new AAIException("AAI_6118", "ModelInvariantId or ModelName or ModelVersionId required if no startNodeFilter data passed."); + } + else { + // Use whatever model info they pass to find start-node instances + // Get the first/top named-query-element used by this query + if( passedModelVersionId != null && !passedModelVersionId.equals("") ){ + // Need to look up the model-invariant-id and model-version to check against persona data + Vertex modVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", + "model-version-id", passedModelVersionId); + Vertex modVtx = getModelGivenModelVer( modVerVtx, "" ); + String calcModId = modVtx.<String>property("model-invariant-id").orElse(null); + // Now we can look up instances that match this model's info + if( calcModId != null ){ + startVerts = this.engine.asAdmin().getReadOnlyTraversalSource().V().has("model-invariant-id-local",calcModId).has("model-version-id-local",passedModelVersionId); + } + } + else if( passedModelInvId != null && !passedModelInvId.equals("") ){ + // They gave us the model-invariant-id + startVerts = this.engine.asAdmin().getReadOnlyTraversalSource().V().has("model-invariant-id-local",passedModelInvId); + } + else if( passedModelName != null && !passedModelName.equals("") ){ + List<Vertex> modelVerVtxList = getModelVersUsingName(transId, fromAppId, passedModelName); + List<Vertex> startVtxList = new ArrayList<>(); + // Need to look up the model-inv-ids and model-versions to check against persona data + if( !modelVerVtxList.isEmpty() ){ + for( int i = 0; i < modelVerVtxList.size(); i++ ){ + String calcModVerId = (modelVerVtxList.get(i)).<String>property("model-version-id").orElse(null); + Vertex modVtx = getModelGivenModelVer(modelVerVtxList.get(i),""); + String calcModInvId = modVtx.<String>property("model-invariant-id").orElse(null); + // Now we can look up instances that match this model's info + Iterator<Vertex> tmpStartIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has("model-invariant-id-local",calcModInvId).has("model-version-id-local",calcModVerId); + while( tmpStartIter.hasNext() ){ + Vertex tmpStartVert = (Vertex) tmpStartIter.next(); + startVtxList.add(tmpStartVert); + } + } + } + if( !startVtxList.isEmpty() ){ + startVerts = startVtxList.iterator(); + } + } + } + + if( startVerts != null ){ + while( startVerts.hasNext() ){ + Vertex tmpStartVert = (Vertex) startVerts.next(); + String vid = tmpStartVert.id().toString(); + String tmpModId = tmpStartVert.<String>property("model-invariant-id-local").orElse(null); + String tmpModVerId = tmpStartVert.<String>property("model-version-id-local").orElse(null); + startVertInfo.put(vid, tmpModVerId); + } + } + if( startVertInfo.isEmpty() ){ + throw new AAIException("AAI_6114", "Start Node(s) could not be found for model data passed. " + + "(modelVersionId = [" + passedModelVersionId + + "], modelInvariantId = [" + passedModelInvId + + "], modelName = [" + passedModelName + + "])"); + } + + return startVertInfo; + } + else { + // Use start-node filter info to find start-node(s) - Note - there could also be model info passed that we'll need + // to use to trim down the set of start-nodes that we find based on the startNodeFilter data. + String modTopNodeType =""; + String modInfoStr = ""; + if( passedModelVersionId != null && !passedModelVersionId.equals("") ){ + modTopNodeType = getModelVerTopWidgetType( transId, fromAppId, passedModelVersionId, "", "" ); + modInfoStr = "modelVersionId = (" + passedModelVersionId + ")"; + } + else if( passedModelInvId != null && !passedModelInvId.equals("") ){ + modTopNodeType = getModelVerTopWidgetType( transId, fromAppId,"", passedModelInvId, "" ); + modInfoStr = "modelId = (" + passedModelInvId + ")"; + } + else if( passedModelName != null && !passedModelName.equals("") ){ + modTopNodeType = getModelVerTopWidgetType( transId, fromAppId,"", "", passedModelName ); + modInfoStr = "modelName = (" + passedModelName + ")"; + } + + if( modTopNodeType.equals("") ){ + if( (passedTopNodeType == null) || passedTopNodeType.equals("") ){ + String msg = "Could not determine the top-node nodeType for this request. modelInfo: [" + modInfoStr + "]"; + throw new AAIException("AAI_6118", msg); + } + else { + // We couldn't find a top-model-type based on passed in model info, but they + // gave us a type to use -- so use it. + modTopNodeType = passedTopNodeType; + } + } + else { + // we did get a topNode type based on model info - make sure it doesn't contradict + // the passsed-in one (if there is one) + if( passedTopNodeType != null && !passedTopNodeType.equals("") + && !passedTopNodeType.equals(modTopNodeType) ){ + throw new AAIException("AAI_6120", "topNodeType passed in [" + passedTopNodeType + + "] does not match nodeType derived for model info passed in: [" + + modTopNodeType + "]"); + } + } + + List<String> modelVersionIds2Check = new ArrayList<>(); + if( (passedModelName != null && !passedModelName.equals("")) ){ + // They passed a modelName, so find all the model UUIDs (model-version-id's) that map to this + modelVersionIds2Check = getModelVerIdsUsingName(transId, fromAppId, passedModelName); + } + if( (passedModelVersionId != null && !passedModelVersionId.equals("")) ){ + // They passed in a modelNameVersionId + if( modelVersionIds2Check.isEmpty() ){ + // There was no modelName passed, so we can use the passed modelNameVersionId + modelVersionIds2Check.add(passedModelVersionId); + } + else if( modelVersionIds2Check.contains(passedModelVersionId) ){ + // The passed in uuid does not conflict with what we got using the passed-in modelName. + // We'll just use the passed in uuid in this case. + // Hopefully they would not be passing strange combinations like this, but we'll try to deal with it. + modelVersionIds2Check = new ArrayList<>(); // Clear out what we had + modelVersionIds2Check.add(passedModelVersionId); + } + } + + // We should now be OK with our topNodeType for this request, so we can look for the actual startNodes + for( int i=0; i < startNodeFilterArrayOfHashes.size(); i++ ){ + // Locate the starting node which will be used to look which corresponds to this set of filter data + Vertex startVtx = null; + try { + Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(modTopNodeType, startNodeFilterArrayOfHashes.get(i)); + if (!result.isPresent()) { + throw new AAIException("AAI_6114", "No Node of type " + modTopNodeType + " found for properties"); + } + startVtx = result.get(); + } + catch( AAIException e ){ + String msg = "Could not find startNode of type = [" + modTopNodeType + "], given these params: " + + startNodeFilterArrayOfHashes.get(i) + ". msg # from getUniqueNode() = " + e.getMessage(); + throw new AAIException("AAI_6114", msg); + } + + String vid = startVtx.id().toString(); + String personaModInvId = startVtx.<String>property("model-invariant-id-local").orElse(null); + String personaModVerId = startVtx.<String>property("model-version-id-local").orElse(null); + + // Either this start-node has persona info (which should not contradict any passed-in model info) + // or they should have passed in the model to use - so we'd just use that. + if( personaModVerId != null && !personaModVerId.equals("") ){ + // There is persona data in this start-node. So make sure it doesn't contradict any "passed" stuff + if( modelVersionIds2Check.isEmpty() + && (passedModelInvId == null || passedModelInvId.equals("")) ){ + // They didn't pass any model info, so use the persona one. + startVertInfo.put(vid, personaModVerId); + } + else if( modelVersionIds2Check.isEmpty() + && (passedModelInvId != null && !passedModelInvId.equals("")) ){ + // They passed in just the modelId - so check it + if( passedModelInvId.equals(personaModInvId) ){ + startVertInfo.put(vid, personaModVerId); + } + } + else if( !modelVersionIds2Check.isEmpty() + && (passedModelInvId == null || passedModelInvId.equals("")) ){ + // They passed in just modelVersionId - so check + if( modelVersionIds2Check.contains(personaModVerId) ){ + startVertInfo.put(vid, personaModVerId); + } + } + else if( !modelVersionIds2Check.isEmpty() + && (passedModelInvId != null && !passedModelInvId.equals("")) ){ + // We have BOTH a modelVersionIds and a modelId to check + if( passedModelInvId.equals(personaModInvId) + && modelVersionIds2Check.contains(personaModVerId) ){ + startVertInfo.put(vid, personaModVerId); + } + } + } + else { + // This start node did not have persona info -- so we will use the passed in model info if they passed one + if( passedModelVersionId!= null && !passedModelVersionId.equals("") ){ + // The model-version-id uniquely identifies a model-ver, so we can use it. + startVertInfo.put(vid, passedModelVersionId); + } + else { + throw new AAIException("AAI_6118", "Found startNode but since it does not have persona data, the " + + " model-version-id is required. "); + } + } + } + } + + return startVertInfo; + + }//end of getStartNodesAndModVersionIds() + + + /** + * Query by model. (really model-ver) + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelVersionId the model-version-id (unique id in model-ver) + * @param modelInvariantId the model-invariant-id (unique id in model) + * @param modelName the model name + * @param topNodeType - optional (needed if neither model-invariant-id nor model-version-id is passed) + * @param startNodeFilterArrayOfHashes the start node filter array of hashes -- optional (used to locate the first node(s) of instance data) + * @param apiVer the api ver + * @return resultSet + * @throws AAIException the AAI exception + */ + public List<ResultSet> queryByModel( String transId, String fromAppId, + String modelVersionId, + String modelInvariantId, + String modelName, + String topNodeType, + List<Map<String,Object>> startNodeFilterArrayOfHashes, + String apiVer ) + throws AAIException{ + + final String transId_f = transId; + final String fromAppId_f = fromAppId; + final String modelVersionId_f = modelVersionId; + final String modelInvId_f = modelInvariantId; + final String modelName_f = modelName; + final String topNodeType_f = topNodeType; + final List<Map<String,Object>> startNodeFilterArrayOfHashes_f = startNodeFilterArrayOfHashes; + final String apiVer_f = apiVer; + + // Find out what our time-limit should be + int timeLimitSec = 0; + String timeLimitString = AAIConfig.get("aai.model.query.timeout.sec"); + if( timeLimitString != null && !timeLimitString.equals("") ){ + try { + timeLimitSec = Integer.parseInt(timeLimitString); + } + catch ( Exception nfe ){ + // Don't worry, we will leave the limit as zero - which tells us not to use it. + } + } + + if( timeLimitSec <= 0 ){ + // We will NOT be using a timer + return queryByModel_Timed( transId, fromAppId, + modelVersionId, + modelInvariantId, + modelName, + topNodeType, + startNodeFilterArrayOfHashes, + apiVer ); + } + + List<ResultSet> resultList = new ArrayList<>(); + TimeLimiter limiter = new SimpleTimeLimiter(); + try { + resultList = limiter.callWithTimeout(new Callable <List<ResultSet>>() { + public List<ResultSet> call() throws AAIException { + return queryByModel_Timed( transId_f, fromAppId_f, + modelVersionId_f, + modelInvId_f, + modelName_f, + topNodeType_f, + startNodeFilterArrayOfHashes_f, + apiVer_f ); + } + }, timeLimitSec, TimeUnit.SECONDS, true); + } + catch (AAIException ae) { + // Re-throw AAIException so we get can tell what happened internally + throw ae; + } + catch (UncheckedTimeoutException ute) { + throw new AAIException("AAI_6140", "Query Processing Limit exceeded. (limit = " + timeLimitSec + " seconds)"); + } + catch (Exception e) { + throw new AAIException("AAI_6128", "Unexpected exception in queryByModel(): " + e.getMessage() ); + } + + return resultList; + } + + + /** + * Query by model (model-ver) timed. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelVersionId the model-version-id (unique id in model-ver) + * @param modelInvariantId the model-invariant-id (unique id in model) + * @param modelName the model name + * @param topNodeType the top node type + * @param startNodeFilterArrayOfHashes the start node filter array of hashes + * @param apiVer the api ver + * @return the array list + * @throws AAIException the AAI exception + */ + public List<ResultSet> queryByModel_Timed( String transId, String fromAppId, + String modelVersionId, + String modelInvariantId, + String modelName, + String topNodeType, + List<Map<String,Object>> startNodeFilterArrayOfHashesVal, + String apiVer ) + throws AAIException{ + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + List<ResultSet> resultArray = new ArrayList<>(); + + // NOTE: this method can be used for different styles of queries: + // a) They could pass neither a modelVersionId or a modelInvariantId but just pass a set of data defining start-nodes. + // Note - with no model info, we need them to pass the startNodeType for us to be able to use the + // start-node-filter data. We would look at each start node and ensure that each has persona-model info. + // Then use whatever model corresponds to each instance to pull that instance's data. + // b) They could pass a modelInvariantId, but no modelVersionId and no startNode info. In this case, we + // Would look in the database for all nodes that have a model-invariant-id-local that matches what was + // passed, and then for each of those instances, pull the data based on the corresponding model. + // c) They could pass a model-version-id, but no startNode info. We'd make sure that if a + // model-invariant-id was also passed, that it does not conflict - but it really should be null if they + // are passing a full model-version-id. Like case -b-, we'd do a query for all nodes + // that have persona info that corresponds to the model-version-id passed and then + // collect data for each one. + // d) They could pass either modelVersionId or modelInvariantId AND startNodeFilter info. In this case we + // would look at the model info to figure out what the top-node-type is, then look at the + // top-node instances based on the startNodeFilter. We'd only collect data for each instance if + // it's persona model info matches what was passed in. + + + // Sorry to do this, but code that gets called with an empty hash as the first array element was causing errors + List<Map<String,Object>> startNodeFilterArrayOfHashes = new ArrayList <Map<String,Object>>(); + if( !startNodeFilterArrayOfHashesVal.isEmpty() ){ + Map<String,Object> tmpH = startNodeFilterArrayOfHashesVal.get(0); + if( !tmpH.isEmpty() ){ + for( int i=0; i < startNodeFilterArrayOfHashesVal.size(); i++ ){ + startNodeFilterArrayOfHashes.add( startNodeFilterArrayOfHashesVal.get(i) ); + } + } + } + + // ---------------------------------------------------------------------------------------------------------- + // Get a Hash of all the start-nodes (top instance-data node for a model-ver where we will + // start collecting data) for startNode2ModelVerHash: + // key = vertex-id for the startNode, + // value = model-version-id for the corresponding model-ver + // ---------------------------------------------------------------------------------------------------------- + Map<String, String> startNode2ModelVerHash = getStartNodesAndModVersionIds( transId, fromAppId, + modelVersionId, modelInvariantId, modelName, topNodeType, + startNodeFilterArrayOfHashes, apiVer ); + + //System.out.println("\nDEBUG -- Here's a dump of the startnodes/model-vers: " + startNode2ModelVerHash.toString()); + + // -------------------------------------------------------------------------------------------------------- + // Figure out what-all models (model-ver nodes) we will be dealing with + // Note - Instances must all use the same type of start-node, but do not have to all use the same model-ver. + // -------------------------------------------------------------------------------------------------------- + Map<String, Vertex> distinctModelVersHash = new HashMap<>(); + // For distinctModelVersHash: key = modelVersionId, val= modelVerVertex + String startNodeType = ""; + if( topNodeType != null && !topNodeType.equals("") ){ + startNodeType = topNodeType; + } + + List<String> skipModelVerIdList = new ArrayList<>(); + List<String> skipStartVertVerIdList = new ArrayList<>(); + Set <String> snKeySet = startNode2ModelVerHash.keySet(); + Iterator<String> startNodeIterator = snKeySet.iterator(); + while( startNodeIterator.hasNext() ){ + String modVerIdKey = (String) startNodeIterator.next(); + String modVerId = startNode2ModelVerHash.get(modVerIdKey); + if( !distinctModelVersHash.containsKey(modVerId) ){ + // First time seeing this model-version-id + Vertex modVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", + "model-version-id", modVerId); + String tmpNodeType = ""; + try { + tmpNodeType = getModelVerTopWidgetType( modVerVtx, "" ); + } + catch( AAIException ae ){ + // There must be some old bad data in the db - we will skip over this model-ver since its + // model is not good anymore - but will log that this is happening. + skipModelVerIdList.add(modVerId); + skipStartVertVerIdList.add(modVerIdKey); + System.out.println(">>> WARNING - will not collect model data for this vertex since " + + "it uses an inconsistant model-ver model. Model-version-id = " + modVerId ); + } + + if( tmpNodeType != null && !tmpNodeType.equals("") ){ + if( startNodeType.equals("") ){ + startNodeType = tmpNodeType; + } + else if( !startNodeType.equals(tmpNodeType) ){ + String msg = "Conflict between startNode types for models involved: [" + startNodeType + + "], [" + tmpNodeType + "]"; + throw new AAIException("AAI_6125", msg); + } + distinctModelVersHash.put(modVerId, modVerVtx); + } + } + } + + //System.out.println("\nDEBUG -- Here's a dump of the DISTINCT model-ver hash: " + distinctModelVersHash.toString() ); + + // ------------------------------------------------------------------------------------------------------ + // Get the "valid-next-step" hash for each distinct model-ver + // While we're at it, get a mapping of model-invariant-id|model-version to model-version-id for + // the model-vers being used + // ------------------------------------------------------------------------------------------------------ + Map<String, Multimap<String, String>> validNextStepHash = new HashMap<>(); + // validNextStepHash: key = modelVerId, value = nextStepMap + Set <String> keySet = distinctModelVersHash.keySet(); + Iterator<String> modelVerIterator = keySet.iterator(); + while( modelVerIterator.hasNext() ){ + String modVerKey = (String) modelVerIterator.next(); + if( ! skipModelVerIdList.contains(modVerKey) ){ + Vertex modelVerVtx = (Vertex)distinctModelVersHash.get(modVerKey); + Multimap<String, String> tmpTopoMap = genTopoMap4ModelVer( transId, fromAppId, + modelVerVtx, modVerKey, dbMaps ); + validNextStepHash.put(modVerKey, tmpTopoMap); + } + } + + // ------------------------------------------------------------------------------------------------- + // Figure out what the "start-node" for each instance will be (plus the info we will use to + // represent that in our topology) + // ------------------------------------------------------------------------------------------------- + List<String> failedPersonaCheckVids = new ArrayList<>(); + Map<String, String> firstStepInfoHash = new HashMap<>(); + // For firstStepInfoHash: key = startNodeVtxId, val=topNodeType plus personaData if applicable + // ie. the value is what we'd use as the "first-step" for this model. + if( !nodeTypeSupportsPersona( startNodeType, dbMaps) ){ + // This node type doesn't have persona info, so we just use startNodeType for the first-step-info + snKeySet = startNode2ModelVerHash.keySet(); + startNodeIterator = snKeySet.iterator(); + while( startNodeIterator.hasNext() ){ + String vtxKey = (String) startNodeIterator.next(); + firstStepInfoHash.put(vtxKey,startNodeType); + } + } + else { + // Need to check that this node's persona data is good and if it is - use it for the first step info + snKeySet = startNode2ModelVerHash.keySet(); + startNodeIterator = snKeySet.iterator(); + while( startNodeIterator.hasNext() ){ + String vtxKey = (String) startNodeIterator.next(); + Iterator<Vertex> vtxIterator = this.engine.asAdmin().getReadOnlyTraversalSource().V(vtxKey); + Vertex tmpVtx = (Vertex)vtxIterator.next(); + String thisVtxModelVerId = startNode2ModelVerHash.get(vtxKey); + if( skipModelVerIdList.contains(thisVtxModelVerId) ){ + // Skip this vertex because it uses a model-ver that is bad + continue; + } + Vertex modelVerVtx = (Vertex)distinctModelVersHash.get(thisVtxModelVerId); + Vertex modelVtx = getModelGivenModelVer( modelVerVtx, "" ); + String modInvId = modelVtx.<String>property("model-invariant-id").orElse(null); + String personaModInvId = tmpVtx.<String>property("model-invariant-id-local").orElse(null); + String personaModVerId = tmpVtx.<String>property("model-version-id-local").orElse(null); + if( modInvId.equals(personaModInvId) && thisVtxModelVerId.equals(personaModVerId) ){ + String tmpPersonaInfoStr = startNodeType + "," + personaModInvId + "," + personaModVerId; + firstStepInfoHash.put(vtxKey, tmpPersonaInfoStr ); + } + else { + // we won't use this start node below when we collect data because it should have + // had persona data that matched it's model - but it did not. + failedPersonaCheckVids.add(vtxKey); + } + } + } + + //System.out.println("\nDEBUG -- Here's a dump of the firstStepInfoHash hash: " + firstStepInfoHash.toString() ); + + // ------------------------------------------------------------------------------------------------ + // Loop through each start-node, collect it's data using collectInstanceData() and put the + // resultSet onto the resultArray. + // ------------------------------------------------------------------------------------------------ + + // Make sure they're not bringing back too much data + String maxString = AAIConfig.get("aai.model.query.resultset.maxcount"); + if( maxString != null && !maxString.equals("") ){ + int maxSets = 0; + try { + maxSets = Integer.parseInt(maxString); + } + catch ( Exception nfe ){ + // Don't worry, we will leave the max as zero - which tells us not to use it. + } + + if( maxSets > 0 && (startNode2ModelVerHash.size() > maxSets) ){ + String msg = " Query returns " + startNode2ModelVerHash.size() + " resultSets. Max allowed is: " + maxSets; + throw new AAIException("AAI_6141", msg); + } + } + + snKeySet = startNode2ModelVerHash.keySet(); + startNodeIterator = snKeySet.iterator(); + while( startNodeIterator.hasNext() ){ + String topNodeVtxId = (String) startNodeIterator.next(); + if( failedPersonaCheckVids.contains(topNodeVtxId) ){ + // Skip this vertex because it failed it's persona-data check above + continue; + } + if( skipStartVertVerIdList.contains(topNodeVtxId) ){ + // Skip this vertex because it uses a model-ver that is bad + continue; + } + + Iterator<Vertex> vtxIterator = this.engine.asAdmin().getReadOnlyTraversalSource().V(topNodeVtxId); + Vertex tmpStartVtx = (Vertex)vtxIterator.next(); + String elementLocationTrail = firstStepInfoHash.get(topNodeVtxId); + String modelVerId = startNode2ModelVerHash.get(topNodeVtxId); + Multimap<String, String> validNextStepMap = validNextStepHash.get(modelVerId); + + List<String> vidsTraversed = new ArrayList<>(); + Map<String,String> emptyDelKeyHash = new HashMap<>(); + Map<String,String> emptyNQElementHash = new HashMap<>(); // Only applies to Named Queries + ResultSet tmpResSet = collectInstanceData( transId, fromAppId, + tmpStartVtx, elementLocationTrail, + validNextStepMap, vidsTraversed, 0, emptyDelKeyHash, emptyNQElementHash, apiVer ); + + resultArray.add(tmpResSet); + } + + return resultArray; + + }// queryByModel_Timed() + + + + /** + * Run delete by model-ver. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelVersionId the model version id -- unique id for a model-ver node + * @param topNodeTypeVal the top node type val -- required if no model-version-id is passed + * @param startNodeFilterHash the start node filter hash -- used to locate the first node of instance data + * @param apiVer the api ver + * @param resVersion the res version -- resourceVersion of the top/first widget in the model instance + * @return HashMap (keys = vertexIds that were deleted) + * @throws AAIException the AAI exception + */ + public Map<String,String> runDeleteByModel( String transId, String fromAppId, + String modelVersionId, String topNodeTypeVal, Map<String,Object> startNodeFilterHash, String apiVer, String resVersion ) + throws AAIException{ + + Map<String,String> retHash = new HashMap<>(); + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + // Locate the Model-ver node to be used + Vertex modelVerVtx = null; + if( modelVersionId != null && !modelVersionId.equals("") ){ + modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", + "model-version-id", modelVersionId); + } + else { + // if they didn't pass the modelVersionId, then we need to use the startNode to figure it out + // Locate the starting node based on the start node params + if( topNodeTypeVal == null || topNodeTypeVal.equals("") ){ + throw new AAIException("AAI_6118", "If no model info is passed, then topNodeType is required. "); + } + + Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(topNodeTypeVal, startNodeFilterHash); + if (!result.isPresent()) { + throw new AAIException("AAI_6114", "No Node of type " + topNodeTypeVal + " found for properties"); + } + Vertex startVtx = result.get(); + + String startVertModVerId = startVtx.<String>property("model-version-id-local").orElse(null); + modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", + "model-version-id", startVertModVerId); + } + + if( modelVerVtx == null ){ + throw new AAIException("AAI_6114", "Could not determine the model-ver for the given input parameters. "); + } + + String topNType = "unknown"; + String modelType = getModelTypeFromModelVer( modelVerVtx, "" ); + + if( modelType.equals("widget") ){ + // If they want to delete using a widget-level model.. That is just a delete of the one + // instance of one of our nodes. + String widgModNodeType = modelVerVtx.<String>property("model-name").orElse(null); + if( (widgModNodeType == null) || widgModNodeType.equals("") ){ + String msg = "Could not find model-name for the widget model [" + modelVersionId + "]."; + throw new AAIException("AAI_6132", msg); + } + Optional<Vertex> result = dbMethHelper.locateUniqueVertex(widgModNodeType, startNodeFilterHash); + if (!result.isPresent()) { + throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties"); + } + Vertex widgetVtx = result.get(); + String widgId = widgetVtx.id().toString(); + serializer.delete(widgetVtx, resVersion, true); + retHash.put(widgId, widgModNodeType); + return retHash; + } + + // --------------------------------------------------------------------------------- + // If we got to here, this must be either a service or resource model. + // So, we'll need to get a Hash of which parts of the model to delete. + // NOTE- deleteByModel is deleting data based on one specific version of a model. + // --------------------------------------------------------------------------------- + String chkFirstNodePersonaModInvId = ""; + String chkFirstNodePersonaModVerId = ""; + String personaData = ""; + Vertex firstModElementVertex = getTopElementForSvcOrResModelVer( modelVerVtx, "" ); + topNType = getModElementWidgetType( firstModElementVertex, "" ); + if( (topNType == null) || topNType.equals("") ){ + String msg = "Could not determine the top-node nodeType for model-version-id: [" + modelVersionId + "]"; + throw new AAIException("AAI_6132", msg); + } + if( nodeTypeSupportsPersona(topNType, dbMaps) ){ + Vertex modelVtx = getModelGivenModelVer(modelVerVtx,""); + chkFirstNodePersonaModInvId = modelVtx.<String>property("model-invariant-id").orElse(null); + chkFirstNodePersonaModVerId = modelVerVtx.<String>property("model-version-id").orElse(null); + personaData = "," + chkFirstNodePersonaModInvId + "," + chkFirstNodePersonaModVerId; + } + + // Get the deleteKeyHash for this model + String incomingTrail = ""; + Map<String, String> currentHash = new HashMap<>(); + Map<String, Vertex> modConHash = new HashMap<>(); + ArrayList <String> vidsTraversed = new ArrayList<>(); + Map<String, String> delKeyHash = collectDeleteKeyHash( transId, fromAppId, + firstModElementVertex, incomingTrail, currentHash, vidsTraversed, + 0, dbMaps, modConHash, + chkFirstNodePersonaModInvId, chkFirstNodePersonaModVerId ); + + + System.out.println("\n ----DEBUG -----: Delete Hash for model: [" + modelVersionId + "] looks like: "); + for( Map.Entry<String, String> entry : delKeyHash.entrySet() ){ + System.out.println("key = [" + entry.getKey() + "], val = [" + entry.getValue() + "]"); + } + System.out.println("\n -----"); + // Locate the starting node that we'll use to start looking for instance data + Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterHash); + if (!result.isPresent()) { + throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties"); + } + Vertex startVtx = result.get(); + if( !chkFirstNodePersonaModInvId.equals("") ){ + // NOTE: For Service or Resource models, if this is a nodeType that supports persona's, then + // we need to make sure that the start node matches the persona values. + String startVertPersonaModInvId = startVtx.<String>property("model-invariant-id-local").orElse(null); + String startVertPersonaModVerId = startVtx.<String>property("model-version-id-local").orElse(null); + if( !chkFirstNodePersonaModInvId.equals(startVertPersonaModInvId) + || !chkFirstNodePersonaModVerId.equals(startVertPersonaModVerId) ){ + String msg = "Persona-Model data mismatch for start node (" + topNType + "), " + + startNodeFilterHash ; + throw new AAIException("AAI_6114", msg); + } + } + String topVid = startVtx.id().toString(); + + // Read the model-ver into a Map for processing + Multimap <String, String> validNextStepMap = genTopoMap4ModelVer(transId, fromAppId, + modelVerVtx, modelVersionId, dbMaps ); + + // Collect the data + String elementLocationTrail = topNType + personaData; + vidsTraversed = new ArrayList<>(); + Map<String,String> emptyHash = new HashMap<>(); + + // Pass emptyHash for the NQElement hash since that parameter only applies to Named Queries + ResultSet retResSet = collectInstanceData( transId, fromAppId, + startVtx, elementLocationTrail, + validNextStepMap, vidsTraversed, 0, delKeyHash, emptyHash, apiVer ); + + // Note: the new ResultSet will have each element tagged with the del flag so we'll know if it + // should be deleted or not - so loop through the results in a try-block since some things + // will get auto-deleted by parents before we get to them --- and try to remove each one. + String vidToResCheck = topVid; + + retHash = deleteAsNeededFromResultSet( transId, fromAppId, retResSet, + vidToResCheck, apiVer, resVersion, emptyHash ); + //String msgStr = "processed deletes for these vids: (\n"+ retHash.keySet().toString() + ")."; + + return retHash; + + }// End of runDeleteByModel() + + + + /** + * Delete as needed from result set. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param resSet the res set + * @param vidToResCheck -- this vertex will need to have its resource-version checked + * @param apiVer the api ver + * @param resVersion the res version + * @param hashSoFar the hash so far -- hash of what's been deleted so far + * @return String + * @throws AAIException the AAI exception + */ + public Map<String,String> deleteAsNeededFromResultSet( String transId, String fromAppId, + ResultSet resSet, String vidToResCheck, String apiVer, String resVersion, Map<String,String> hashSoFar ) + throws AAIException + { + Map<String,String> retHash = new HashMap<>(); + retHash.putAll( hashSoFar ); + Boolean deleteIt = false; + + if( resSet.getVert() == null ){ + return retHash; + } + + TitanVertex thisVtx = resSet.getVert(); + String thisGuyId = ""; + String thisNT = ""; + String thisGuyStr = ""; + + try { + if( thisVtx != null && !thisVtx.isRemoved() ){ + thisGuyId = thisVtx.id().toString(); + thisNT = thisVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + thisGuyStr = thisGuyId + "[" + thisNT + " found at:" + resSet.getLocationInModelSubGraph() + "]"; + } + } + catch (Exception ex) { + // Sometimes things have already been deleted by the time we get to them - just log it. + LOGGER.warn("Exception when deleting " + thisGuyStr + ". msg = " + ex.getMessage(), ex); + } + + if( thisGuyId.equals("") ){ + // The vertex must have already been removed. Just return. + return retHash; + } + else { + if( resSet.getNewDataDelFlag() != null && resSet.getNewDataDelFlag().equals("T") ){ + LOGGER.info(">> will try to delete this one >> " + thisGuyStr); + + try { + Boolean requireResourceVersion = false; + if( thisGuyId.equals(vidToResCheck) ){ + // This is the one vertex that we want to check the resourceId before deleting + requireResourceVersion = true; + } + this.serializer.delete(thisVtx, resVersion, requireResourceVersion); + } + catch (AAIException ae) { + String errorCode = ae.getErrorObject().getErrorCode(); + if ( errorCode.equals("6130") || errorCode.equals("6131") ) { + // They didn't pass the correct resource-version for the top node. + throw ae; + } + else { + String errText = ae.getErrorObject().getErrorText(); + String errDetail = ae.getMessage(); + LOGGER.warn("Exception when deleting " + thisGuyStr + ". ErrorCode = " + errorCode + + ", errorText = " + errText + ", details = " + errDetail); + } + } + catch( Exception e ){ + // We'd expect to get a "node not found" here sometimes depending on the order that + // the model has us finding / deleting nodes. + // Ignore the exception - but log it so we can see what happened. + LOGGER.warn("Exception when deleting " + thisGuyStr + e.getMessage(), e); + } + + // We can't depend on a thrown exception to tell us if a node was deleted since it may + // have been auto=deleted before this removeAaiNode() call. + // --- Not sure if we would want to check anything here -- because the graph.commit() is done outside of this call. + + deleteIt = true; + } + else { + // --- DEBUG ---- + System.out.println(">>>>>>> NOT DELETING THIS ONE >>>> " + thisGuyStr ); + List<String> retArr = dbMethHelper.getVertexProperties(thisVtx); + for( String info : retArr ){ System.out.println(info); } + // --- DEBUG ---- + } + } + + // Now call this routine for the sub-resultSets + List <ResultSet> subResultSetList = resSet.getSubResultSet(); + Iterator <ResultSet> subResSetIter = subResultSetList.iterator(); + while( subResSetIter.hasNext() ){ + ResultSet tmpSubResSet = subResSetIter.next(); + retHash = deleteAsNeededFromResultSet( transId, fromAppId, tmpSubResSet, + vidToResCheck, apiVer, resVersion, retHash ); + } + + if( deleteIt ){ + retHash.put(thisGuyId, thisGuyStr); + } + + return retHash; + + }// deleteAsNeededFromResultSet() + + + + /** + * Query by named query (old version). + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryUuid the named query uuid + * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data + * @param apiVer the api ver + * @return resultSet + * @throws AAIException the AAI exception + */ + public List<ResultSet> queryByNamedQuery( String transId, String fromAppId, + String namedQueryUuid, + ArrayList <Map<String,Object>> startNodeFilterArrayOfHashes, + String apiVer ) + throws AAIException{ + + String dummyCutPoint = null; + Map<String,Object> dummySecondaryFilterHash = null; + + return queryByNamedQuery( transId, fromAppId, + namedQueryUuid, + startNodeFilterArrayOfHashes, + apiVer, + dummyCutPoint, + dummySecondaryFilterHash ); + } + + + /** + * Query by named query. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryUuid the named query uuid + * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data + * @param apiVer the api ver + * @param secondaryCutPoint nodeType where we will prune if secondary filter is not met + * @param secondaryFilterHash secondary filter params + * @return resultSet + * @throws AAIException the AAI exception + */ + public List<ResultSet> queryByNamedQuery( String transId, String fromAppId, + String namedQueryUuid, + List<Map<String,Object>> startNodeFilterArrayOfHashes, + String apiVer, + String secondaryFilterCutPoint, + Map<String,Object> secondaryFilterHash ) + throws AAIException{ + + final String transId_f = transId; + final String fromAppId_f = fromAppId; + final String namedQueryUuid_f = namedQueryUuid; + final List<Map<String,Object>> startNodeFilterArrayOfHashes_f = startNodeFilterArrayOfHashes; + final String apiVer_f = apiVer; + final String secondaryFilterCutPoint_f = secondaryFilterCutPoint; + final Map<String,Object> secondaryFilterHash_f = secondaryFilterHash; + + // Find out what our time-limit should be + int timeLimitSec = 0; + String timeLimitString = AAIConfig.get("aai.model.query.timeout.sec"); + if( timeLimitString != null && !timeLimitString.equals("") ){ + try { + timeLimitSec = Integer.parseInt(timeLimitString); + } + catch ( Exception nfe ){ + // Don't worry, we will leave the limit as zero - which tells us not to use it. + } + } + + if( timeLimitSec <= 0 ){ + // We will NOT be using a timer + return queryByNamedQuery_Timed( transId, fromAppId, + namedQueryUuid, + startNodeFilterArrayOfHashes, + apiVer, + secondaryFilterCutPoint_f, + secondaryFilterHash_f ); + } + + List<ResultSet> resultList = new ArrayList<>(); + TimeLimiter limiter = new SimpleTimeLimiter(); + try { + resultList = limiter.callWithTimeout(new Callable <List<ResultSet>>() { + public List<ResultSet> call() throws AAIException { + return queryByNamedQuery_Timed( transId_f, fromAppId_f, + namedQueryUuid_f, + startNodeFilterArrayOfHashes_f, + apiVer_f, + secondaryFilterCutPoint_f, + secondaryFilterHash_f ); + } + }, timeLimitSec, TimeUnit.SECONDS, true); + + } + catch (AAIException ae) { + // Re-throw AAIException so we get can tell what happened internally + throw ae; + } + catch (UncheckedTimeoutException ute) { + throw new AAIException("AAI_6140", "Query Processing Limit exceeded. (limit = " + timeLimitSec + " seconds)"); + } + catch (Exception e) { + throw new AAIException("AAI_6128", "Unexpected exception in queryByNamedQuery(): " + e.getMessage() ); + } + + return resultList; + } + + + /** + * Query by named query timed. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryUuid the named query uuid + * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data + * @param apiVer the api ver + * @param secondaryFilterCutPoint the nodeType where we will parse for the secondary Filter + * @param secondaryFilterHash the secondary filter hash + * @return resultSet + * @throws AAIException the AAI exception + */ + public List<ResultSet> queryByNamedQuery_Timed( String transId, String fromAppId, + String namedQueryUuid, + List<Map<String,Object>> startNodeFilterArrayOfHashes, + String apiVer, + String secondaryFilterCutPoint, + Map<String,Object> secondaryFilterHash + ) + throws AAIException{ + + // Locate the Query to be used + Vertex queryVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query", + "named-query-uuid", namedQueryUuid); + + // Get the first/top named-query-element used by this query + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, queryVtx, "named-query-element"); + Vertex firstNqElementVert = null; + int count = 0; + String topNType = ""; + while( vertI != null && vertI.hasNext() ){ + firstNqElementVert = vertI.next(); + count++; + topNType = getNqElementWidgetType( transId, fromAppId, firstNqElementVert, "" ); + } + + if( count < 1 ){ + // A named query must start with a single top element + throw new AAIException("AAI_6133", "No top-node defined for named-query-uuid = [" + namedQueryUuid + "]"); + } + else if( count > 1 ){ + // A named query should start with a single top element + throw new AAIException("AAI_6133", "More than one top-node defined for named-query-uuid = [" + namedQueryUuid + "]"); + } + if( (topNType == null) || topNType.equals("") ){ + String msg = "Could not determine the top-node nodeType for Named Query: [" + namedQueryUuid + "]"; + throw new AAIException("AAI_6133", msg); + } + + // Read the topology into a hash for processing + Multimap <String, String> validNextStepMap = genTopoMap4NamedQ(transId, fromAppId, queryVtx, namedQueryUuid); + + List<Vertex> startVertList = new ArrayList<>(); + if( startNodeFilterArrayOfHashes.size() == 1 ){ + // If there is only one set of startFilter info given, then allow it to possibly not be + // defining just one start node. + Map<String, Object> cleanHash = new HashMap<>(); + Map<String, Object> tmpHash = startNodeFilterArrayOfHashes.get(0); + Set <String> propKeySet = tmpHash.keySet(); + Iterator<String> propIter = propKeySet.iterator(); + Introspector obj = loader.introspectorFromName(topNType); + Set<String> keys = obj.getKeys(); + boolean foundIndexedField = false; + int propertiesSet = 0; + while( propIter.hasNext() ){ + String oldVtxKey = (String) propIter.next(); + String newKey = oldVtxKey; + String [] parts = oldVtxKey.split("\\."); + if( parts.length == 2 ){ + newKey = parts[1]; + } + Object obVal = tmpHash.get(oldVtxKey); + if (obj.hasProperty(newKey)) { + if (keys.contains(newKey)) { + foundIndexedField = true; + } + obj.setValue(newKey, obVal); + propertiesSet++; + } + } + //we found all the properties in the startNodeType + if (propertiesSet == propKeySet.size()) { + if (foundIndexedField) { + QueryBuilder builder = this.engine.getQueryBuilder().exactMatchQuery(obj); + startVertList = builder.toList(); + } else { + //force a filter from aai-node-type + QueryBuilder builder = this.engine.getQueryBuilder().createContainerQuery(obj).exactMatchQuery(obj); + startVertList = builder.toList(); + } + } else { + Optional<Vertex> tmpVtx = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterArrayOfHashes.get(0)); + // Only found one, so just use it. + if (tmpVtx.isPresent()) { + startVertList.add(tmpVtx.get()); + } + } + } + else { + // Since they give an array of startNodeFilterHash info, we expect each one + // to just point to one node. + for( int i = 0; i < startNodeFilterArrayOfHashes.size(); i++ ){ + // Locate the starting node for each set of data + Optional<Vertex> tmpVtx = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterArrayOfHashes.get(i)); + if (tmpVtx.isPresent()) { + startVertList.add(tmpVtx.get()); + } + } + } + + if (startVertList.isEmpty()) { + throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties"); + } + // Make sure they're not bringing back too much data + String maxString = AAIConfig.get("aai.model.query.resultset.maxcount"); + if( maxString != null && !maxString.equals("") ){ + int maxSets = Integer.parseInt(maxString); + if( startVertList.size() > maxSets ){ + String msg = " Query returns " + startVertList.size() + " resultSets. Max allowed is: " + maxSets; + throw new AAIException("AAI_6141", msg); + } + } + + // Loop through each start node and get its data + List<ResultSet> resSetList = new ArrayList<>(); + for( int i = 0; i < startVertList.size(); i++ ){ + Vertex startVtx = startVertList.get(i); + // Collect the data + String elementLocationTrail = topNType; + ArrayList <String> vidsTraversed = new ArrayList<>(); + Map<String,String> emptyDelKeyHash = new HashMap<>(); // Does not apply to Named Queries + + // Get the mapping of namedQuery elements to our widget topology for this namedQuery + String incomingTrail = ""; + Map<String, String> currentHash = new HashMap<>(); + + Map<String,String> namedQueryElementHash = collectNQElementHash( transId, fromAppId, + firstNqElementVert, incomingTrail, currentHash, vidsTraversed, 0 ); + + vidsTraversed = new ArrayList<>(); + ResultSet tmpResSet = collectInstanceData( transId, fromAppId, + startVtx, elementLocationTrail, + validNextStepMap, vidsTraversed, 0, emptyDelKeyHash, namedQueryElementHash, apiVer ); + resSetList.add(tmpResSet); + } + + // If a secondary filter was defined, we will prune the collected instance data result set(s) based on it. + List<ResultSet> prunedResSetList = new ArrayList<>(); + if( resSetList != null && !resSetList.isEmpty() ){ + for( int i = 0; i < resSetList.size(); i++ ){ + if( secondaryFilterCutPoint == null || secondaryFilterCutPoint.equals("") || secondaryFilterHash == null ){ + // They didn't want to do any pruning, so just use the results we already had + prunedResSetList.add(resSetList.get(i)); + } + else { + ResultSet tmpResSet = pruneResultSet(resSetList.get(i), secondaryFilterCutPoint, secondaryFilterHash); + if( tmpResSet != null ){ + prunedResSetList.add(tmpResSet); + } + } + } + } + + // Since a NamedQuery can mark some nodes as "do-not-display", we need to collapse our resultSet so + // does not display those nodes. + List<ResultSet> collapsedResSetList = new ArrayList<>(); + if( prunedResSetList != null && !prunedResSetList.isEmpty() ){ + for( int i = 0; i < prunedResSetList.size(); i++ ){ + // Note - a single resultSet could be collapsed into many smaller ones if they + // marked all the "top" node-elements as do-not-output. Ie. the query may + // have had a top-node of "generic-vnf" which joins down to different l-interfaces. + // If they only want to see the l-interfaces, then a single result set + // would be "collapsed" into many separate resultSets - each of which is + // just a single l-interface. + List<ResultSet> tmpResSetList = collapseForDoNotOutput(prunedResSetList.get(i)); + if( tmpResSetList != null && !tmpResSetList.isEmpty() ){ + for( int x = 0; x < tmpResSetList.size(); x++ ){ + //showResultSet( tmpResSetList.get(x), 0 ); //DEBUG-- this was just for testing + collapsedResSetList.add(tmpResSetList.get(x)); + } + } + } + } + + return collapsedResSetList; + + }// End of queryByNamedQuery() + + + /** + * Prune a result set as per a secondary filter. + * + * @param resSetVal the res set val + * @param cutPoint the nodeType where the trim will happen + * @param secFilterHash hash of properties and values to use as the secondary filter + * @return pruned result set + * @throws AAIException the AAI exception + */ + public ResultSet pruneResultSet( ResultSet resSetVal, String cutPointType, Map<String,Object> secFilterHash ) + throws AAIException { + + // Given a ResultSet and some secondary filter info, do pruning as needed + ResultSet pResSet = new ResultSet(); + + // For this ResultSet, we will see if we are on a node of the type that is our cutPoint; + // then only keep it if we peek "below" and see a match for our filter. + + String nt = resSetVal.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( nt != null && nt.equals(cutPointType) ){ + // We are on the type of node that may need to be "pruned" along with it's sub-results + if( ! satisfiesFilters(resSetVal, secFilterHash) ){ + // Return an empty result set since we are pruning at this level. + return pResSet; + } + } + + // If we made it to here, we will not be pruning at this level, so we will + // be returning a copy of this resultSet that has it's subResults pruned (as needed). + pResSet.setVert(resSetVal.getVert()); + pResSet.setDoNotOutputFlag(resSetVal.getDoNotOutputFlag()); + pResSet.setExtraPropertyHash(resSetVal.getExtraPropertyHash()); + pResSet.setLocationInModelSubGraph(resSetVal.getLocationInModelSubGraph()); + pResSet.setNewDataDelFlag(resSetVal.getNewDataDelFlag()); + pResSet.setPropertyLimitDesc(resSetVal.getPropertyLimitDesc()); + pResSet.setPropertyOverRideHash(resSetVal.getPropertyOverRideHash()); + + if( !resSetVal.getSubResultSet().isEmpty() ){ + ListIterator<ResultSet> listItr = resSetVal.getSubResultSet().listIterator(); + List<ResultSet> newSubSetList = new ArrayList<>(); + while( listItr.hasNext() ){ + ResultSet tmpSubResSet = pruneResultSet( listItr.next(), cutPointType, secFilterHash ); + if( tmpSubResSet.getVert() != null ){ + // This one wasn't pruned - so keep it. + newSubSetList.add(tmpSubResSet); + } + } + pResSet.setSubResultSet(newSubSetList); + } + + return pResSet; + + }// End pruneResultSet() + + + /** + * Satisfies hash of filters. + * + * @param resSet the res set + * @param filterHash the filter hash + * @return true, if successful + * @throws AAIException the AAI exception + */ + public boolean satisfiesFilters( ResultSet resSet, Map<String,Object> filterHash ) + throws AAIException { + + if( filterHash.isEmpty() ){ + // Nothing to look for, so no, we didn't find it. + return false; + } + + Iterator <?> it = filterHash.entrySet().iterator(); + while( it.hasNext() ){ + Map.Entry<?,?> filtEntry = (Map.Entry<?,?>) it.next(); + String propNodeTypeDotName = (filtEntry.getKey()).toString(); + String fpv = (filtEntry.getValue()).toString(); + + int periodLoc = propNodeTypeDotName.indexOf("."); + if( periodLoc <= 0 ){ + String emsg = "Bad filter param key passed in: [" + propNodeTypeDotName + "]. Expected format = [nodeName.paramName]\n"; + throw new AAIException("AAI_6120", emsg); + } + else { + String fnt = propNodeTypeDotName.substring(0,periodLoc); + String fpn = propNodeTypeDotName.substring(periodLoc + 1); + if( filterMetByThisSet( resSet, fnt, fpn, fpv ) ){ + //System.out.println(" DEBUG -- satisfied/matched filter: [" + fnt + "|" + fpn + "|" + fpv + "]."); + } + else { + //System.out.println(" DEBUG -- NOT satisfied/matched filter: [" + fnt + "|" + fpn + "|" + fpv + "]."); + return false; + } + } + } + + // Made it through all the filters -- it found what we were looking for. + return true; + + }// end of satisfiesFilters() + + + /** + * Filter met by this set. + * + * @param resSet the res set + * @param filtNodeType the filt node type + * @param filtPropName the filt prop name + * @param filtPropVal the filt prop val + * @return true, if successful + */ + public boolean filterMetByThisSet( ResultSet resSet, String filtNodeType, String filtPropName, String filtPropVal ) { + // Note - we are just looking for a positive match for one filter for this resultSet + // NOTE: we're expecting the filter to have a format like this: "nodeType.parameterName:parameterValue" + + Vertex vert = resSet.getVert(); + if( vert == null ){ + return false; + } + else { + String nt = resSet.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( nt.equals( filtNodeType ) ){ + if( filtPropName.equals("vertex-id") ){ + // vertex-id can't be gotten the same way as other properties + String thisVtxId = vert.id().toString(); + if( thisVtxId.equals(filtPropVal) ){ + return true; + } + } + else { + Object thisValObj = vert.property(filtPropName).orElse(null); + if( thisValObj != null ){ + String thisVal = thisValObj.toString(); + if( thisVal.equals(filtPropVal) ){ + return true; + } + } + } + } + } + + // Didn't find a match at the this level, so check the sets below it meet the criteria + if( resSet.getSubResultSet() != null ){ + ListIterator<ResultSet> listItr = resSet.getSubResultSet().listIterator(); + while( listItr.hasNext() ){ + if( filterMetByThisSet(listItr.next(), filtNodeType, filtPropName, filtPropVal) ){ + return true; + } + } + } + + return false; + + }// end of filterMetByThisSet() + + + + /** + * Collapse for do not output. + * + * @param resSetVal the res set val + * @return the array list + * @throws AAIException the AAI exception + */ + public List<ResultSet> collapseForDoNotOutput( ResultSet resSetVal ) + throws AAIException { + + // Given a ResultSet -- if it is tagged to NOT be output, then replace it with + // it's sub-ResultSets if it has any. + List<ResultSet> colResultSet = new ArrayList<>(); + + if( resSetVal.getDoNotOutputFlag().equals("true") ){ + // This ResultSet isn't to be displayed, so replace it with it's sub-ResultSets + List<ResultSet> subResList = (ArrayList<ResultSet>) resSetVal.getSubResultSet(); + for( int k = 0; k < subResList.size(); k++ ){ + List<ResultSet> newSubResList = collapseForDoNotOutput(subResList.get(k)); + colResultSet.addAll(newSubResList); + } + } + else { + // This set will be displayed + colResultSet.add(resSetVal); + } + + // For each result set now at this level, call this same routine to collapse their sub-resultSets + for( int i = 0; i < colResultSet.size(); i++ ){ + List<ResultSet> newSubSet = new ArrayList<>(); + List<ResultSet> subResList = (ArrayList<ResultSet>) colResultSet.get(i).getSubResultSet(); + for( int n = 0; n < subResList.size(); n++ ){ + List<ResultSet> newSubResList = collapseForDoNotOutput(subResList.get(n)); + newSubSet.addAll(newSubResList); + } + // Replace the old subResultSet with the collapsed set + colResultSet.get(i).setSubResultSet(newSubSet); + } + + return colResultSet; + + }// End collapseForDoNotOutput() + + + + /** + * Collect instance data. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param thisLevelElemVtx the element vtx at this level + * @param thisVertsTrail the this verts trail + * @param elementLocationTrail -- trail of nodeTypes that got us here (this element vertex) from the top + * @param validNextStepMap the valid next step map -- hash of valid next steps (node types) for this model + * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point + * @param levelCounter the level counter + * @param delKeyHash -- hashMap of which spots on our topology should be deleted during a modelDelete + * @param namedQueryElementHash - hashMap which maps each spot in our widget topology to the NamedQueryElemment that it maps to + * @param apiVer the api ver + * @return resultSet + * @throws AAIException the AAI exception + */ + public ResultSet collectInstanceData( String transId, String fromAppId, + Vertex thisLevelElemVtx, + String thisVertsTrail, + Multimap <String,String> validNextStepMap, + List<String> vidsTraversed, + int levelCounter, + Map<String,String> delKeyHash, // only applies when collecting data using the default model for delete + Map<String,String> namedQueryElementHash, // only applies to named-query data collecting + String apiVer + ) throws AAIException { + + levelCounter++; + + String thisElemVid = thisLevelElemVtx.id().toString(); + + if( levelCounter > MAX_LEVELS ) { + throw new AAIException("AAI_6125", "collectInstanceData() has looped across more levels than allowed: " + MAX_LEVELS + ". "); + } + + ResultSet rs = new ResultSet(); + if( namedQueryElementHash.containsKey(thisVertsTrail) ){ + // We're collecting data for a named-query, so need to see if we need to do anything special + String nqElUuid = namedQueryElementHash.get(thisVertsTrail); + Vertex nqElementVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query-element", + "named-query-element-uuid", nqElUuid); + + String tmpDoNotShow = nqElementVtx.<String>property("do-not-output").orElse(null); + if( tmpDoNotShow != null && tmpDoNotShow.equals("true") ){ + rs.setDoNotOutputFlag("true"); + } + + if( namedQueryConstraintSaysStop(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer) ){ + // There was a property constraint which says they do not want to collect this vertex or whatever + // might be below it. Just return the empty rs here. + return rs; + } + + String propLimDesc = nqElementVtx.<String>property("property-limit-desc").orElse(null); + if( (propLimDesc != null) && !propLimDesc.equals("") ){ + if (propLimDesc.equalsIgnoreCase("show-all")) { + rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_ALL); + } else if (propLimDesc.equalsIgnoreCase("show-none")) { + rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_NONE); + }else if (propLimDesc.equalsIgnoreCase("name-and-keys-only")) { + rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY); + } + } + + // Look to see if we need to use an Override of the normal properties + Map<String,Object> tmpPropertyOverRideHash = getNamedQueryPropOverRide(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer); + //System.out.println(" DEBUG --- USING this propertyOverride data set on ResSet [" + tmpPropertyOverRideHash.toString() + "]"); + rs.setPropertyOverRideHash(tmpPropertyOverRideHash); + + // See if we need to look up any "unconnected" data that needs to be associated with this result set + Map<String,Object> tmpExtraPropHash = getNamedQueryExtraDataLookup(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer); + //System.out.println(" DEBUG --- ADDING this EXTRA Lookup data to the ResSet [" + tmpExtraPropHash.toString() + "]"); + rs.setExtraPropertyHash(tmpExtraPropHash); + } + + rs.setVert((TitanVertex)thisLevelElemVtx); + rs.setLocationInModelSubGraph(thisVertsTrail); + if( delKeyHash.containsKey(thisVertsTrail) && delKeyHash.get(thisVertsTrail).equals("T") ){ + rs.setNewDataDelFlag("T"); + } + else { + rs.setNewDataDelFlag("F"); + } + + // Use Gremlin-pipeline to just look for edges that go to a valid "next-steps" + Collection <String> validNextStepColl = validNextStepMap.get(thisVertsTrail); + + // Because of how we process linkage-points, we may have duplicate node-types in our next-stepMap (for one step) + // So, to keep from looking (and bringing back) the same data twice, we need to make sure our next-steps are unique + Set<String> validNextStepHashSet = new HashSet<>(); + Iterator <String> ntcItr = validNextStepColl.iterator(); + while( ntcItr.hasNext() ){ + String targetStepStr = ntcItr.next(); + validNextStepHashSet.add(targetStepStr); + } + + List<String> tmpVidsTraversedList = new ArrayList<>(); + tmpVidsTraversedList.addAll(vidsTraversed); + tmpVidsTraversedList.add(thisElemVid); + + Iterator <String> ntItr = validNextStepHashSet.iterator(); + while( ntItr.hasNext() ){ + String targetStep = ntItr.next(); + // NOTE: NextSteps can either be just a nodeType, or can be a nodeType plus + // model-invariant-id-local and model-version-id-local (the two persona properties) + // if those need to be checked also. + // When the persona stuff is part of the step, it is a comma separated string. + // Ie. "nodeType,model-inv-id-local,model-version-id-local" (the two "persona" props) + // + String targetNodeType = ""; + String pmid = ""; + String pmv = ""; + Boolean stepIsJustNT = true; + if( targetStep.contains(",") ){ + stepIsJustNT = false; + String[] pieces = targetStep.split(","); + if( pieces.length != 3 ){ + throw new AAIException("AAI_6128", "Unexpected format for nextStep in model processing = [" + + targetStep + "]. "); + } + else { + targetNodeType = pieces[0]; + pmid = pieces[1]; + pmv = pieces[2]; + } + } + else { + // It's just the nodeType with no other info + targetNodeType = targetStep; + } + + GraphTraversal<Vertex, Vertex> modPipe = null; + if( stepIsJustNT ){ + modPipe = this.engine.asAdmin().getReadOnlyTraversalSource().V(thisLevelElemVtx).both().has(AAIProperties.NODE_TYPE, targetNodeType); + } + else { + modPipe = this.engine.asAdmin().getReadOnlyTraversalSource().V(thisLevelElemVtx).both().has(AAIProperties.NODE_TYPE, targetNodeType).has("model-invariant-id-local",pmid).has("model-version-id-local",pmv); + } + + if( modPipe == null || !modPipe.hasNext() ){ + //System.out.println("DEBUG - didn't find any [" + targetStep + "] connected to this guy (which is ok)"); + } + else { + while( modPipe.hasNext() ){ + Vertex tmpVert = (Vertex) modPipe.next(); + String tmpVid = tmpVert.id().toString(); + String tmpTrail = thisVertsTrail + "|" + targetStep; + if( !vidsTraversed.contains(tmpVid) ){ + // This is one we would like to use - so we'll include the result set we get for it + ResultSet tmpResSet = collectInstanceData( transId, fromAppId, + tmpVert, tmpTrail, + validNextStepMap, tmpVidsTraversedList, + levelCounter, delKeyHash, namedQueryElementHash, apiVer ); + + rs.getSubResultSet().add(tmpResSet); + } + } + } + } + + return rs; + + } // End of collectInstanceData() + + + /** + * Gen topo map 4 model. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelVerVertex the model-ver vertex + * @param modelVerId the model-version-id + * @param dbMaps the db maps + * @return MultiMap of valid next steps for each potential model-element + * @throws AAIException the AAI exception + */ + public Multimap<String, String> genTopoMap4ModelVer( String transId, String fromAppId, + Vertex modelVerVertex, String modelVerId, DbMaps dbMaps ) + throws AAIException { + + if( modelVerVertex == null ){ + throw new AAIException("AAI_6114", "null modelVerVertex passed to genTopoMap4ModelVer()"); + } + + Multimap <String, String> initialEmptyMap = ArrayListMultimap.create(); + List<String> vidsTraversed = new ArrayList<>(); + String modelType = getModelTypeFromModelVer( modelVerVertex, "" ); + if( modelType.equals("widget") ){ + // A widget model by itself does not have a topoplogy. That is - it has no "model-elements" which + // define how it is connected to other things. All it has is a name which ties it to + // an aai-node-type + Iterator<Vertex> vertI= this.traverseIncidentEdges(EdgeType.TREE, modelVerVertex, "model-element"); + if( vertI != null && vertI.hasNext() ){ + throw new AAIException("AAI_6132", "Bad Model Definition: Widget Model has a startsWith edge to a model-element. " + + " model-version-id = " + modelVerId); + } + else { + return initialEmptyMap; + } + } + + String firstModelVerId = modelVerVertex.<String>property("model-version-id").orElse(null); + String firstModelVersion = modelVerVertex.<String>property("model-version").orElse(null); + if( firstModelVerId == null || firstModelVerId.equals("") || firstModelVersion == null || firstModelVersion.equals("") ){ + throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-version-id or model-version. model-version-id = " + + modelVerId); + } + + Vertex firstElementVertex = getTopElementForSvcOrResModelVer( modelVerVertex, "" ); + Vertex firstEleModVerVtx = getModelVerThatElementRepresents( firstElementVertex, "" ); + String firstElemModelType = getModelTypeFromModelVer( firstEleModVerVtx, "" ); + if( ! firstElemModelType.equals("widget") ){ + throw new AAIException("AAI_6132", "Bad Model Definition: First element must correspond to a widget type model. Model UUID = " + + modelVerId); + } + + Vertex firstModVtx = getModelGivenModelVer( modelVerVertex, "" ); + String firstModelInvId = firstModVtx.<String>property("model-invariant-id").orElse(null); + if( firstModelInvId == null || firstModelInvId.equals("") ){ + throw new AAIException("AAI_6132", "Bad Model Definition: Could not find model.model-invariant-id given model-ver.model-version-id = " + + modelVerId); + } + + Multimap <String, String> collectedMap = collectTopology4ModelVer( transId, fromAppId, + firstElementVertex, "", initialEmptyMap, vidsTraversed, 0, dbMaps, null, firstModelInvId, firstModelVersion ); + + return collectedMap; + + } // End of genTopoMap4ModelVer() + + + public List<String> makeSureItsAnArrayList( String listStringVal ){ + // We're sometimes getting a String back on db properties that should be ArrayList<String> + // Seems to be how they're defined in OXM - whether they use a "xml-wrapper" or not + // Need to translate them into ArrayLists sometimes... + + List<String> retArrList = new ArrayList<String>(); + String listString = listStringVal; + listString = listString.replace(" ", ""); + listString = listString.replace("\"", ""); + listString = listString.replace("[", ""); + listString = listString.replace("]", ""); + String [] pieces = listString.split(","); + if( pieces != null && pieces.length > 0 ){ + for( int i = 0; i < pieces.length; i++ ){ + retArrList.add(pieces[i]); + } + } + + return retArrList; + } + + + /** + * Gets the mod constraint hash. + * + * @param modelElementVtx the model element vtx + * @param currentHash -- the current ModelConstraint's that this routine will add to if it finds any. + * @return HashMap of model-constraints that will be looked at for this model-element and what's "below" it. + * @throws AAIException the AAI exception + */ + public Map<String, Vertex> getModConstraintHash( Vertex modelElementVtx, Map<String, Vertex> currentHash ) + throws AAIException { + + // For a given model-element vertex, look to see if there are any "model-constraint" elements that is has + // an OUT "uses" edge to. If it does, then get any "constrained-element-set" nodes that are pointed to + // by the "model-constraint". That will be the replacement "constrained-element-set". The UUID of the + // "constrained-element-set" that it is supposed to replace is found in the property: + // model-constraint.constrained-element-set-uuid-to-replace + // + // For now, that is the only type of model-constraint allowed, so that is all we will look for. + // Pass back any of these "constrained-element-set" nodes along with any that were passed in by + // the "currentHash" parameter. + + if( modelElementVtx == null ){ + String msg = " null modelElementVtx passed to getModConstraintHash() "; + throw new AAIException("AAI_6114", msg); + } + + String modelType = modelElementVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( modelType == null || (!modelType.equals("model-element")) ){ + String msg = " getModConstraintHash() called with wrong type model: [" + modelType + "]. "; + throw new AAIException("AAI_6114", msg); + } + + Map<String, Vertex> thisHash = new HashMap<>(); + if( currentHash != null ){ + thisHash.putAll(currentHash); + } + + int count = 0; + List<Vertex> modelConstraintArray = new ArrayList<>(); + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, modelElementVtx, "model-constraint"); + while( vertI != null && vertI.hasNext() ){ + Vertex tmpVert = vertI.next(); + String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( (connectToType != null) && connectToType.equals("model-constraint") ){ + // We need to find the constrained element set pointed to by this and add it to the Hash to return + modelConstraintArray.add(tmpVert); + count++; + } + } + + if( count > 0 ) { + for( int i = 0; i < count; i++ ){ + Vertex vtxOfModelConstraint = modelConstraintArray.get(i); + String uuidOfTheOneToBeReplaced = vtxOfModelConstraint.<String>property("constrained-element-set-uuid-2-replace").orElse(null); + // We have the UUID of the constrained-element-set that will be superseded, now find the + // constrained-element-set to use in its place + Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.TREE, vtxOfModelConstraint, "constrained-element-set"); + while( mvertI != null && mvertI.hasNext() ){ + // There better only be one... + Vertex tmpVert = mvertI.next(); + String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( (connectToType != null) && connectToType.equals("constrained-element-set") ){ + // This is the "constrained-element-set" that we want to use as the Replacement + thisHash.put(uuidOfTheOneToBeReplaced, tmpVert ); + } + } + } + return thisHash; + } + else { + // Didn't find anything to add, so just return what they passed in. + return currentHash; + } + + } // End of getModConstraintHash() + + + /** + * Gets the top element vertex for service or resource model. + * + * @param modelVerVtx the model-ver vertex + * @return first element pointed to by this model-ver + * @throws AAIException the AAI exception + */ + public Vertex getTopElementForSvcOrResModelVer( Vertex modelVerVtx, String trail ) + throws AAIException { + + // For a "resource" or "service" type model, return the "top" element in that model + if( modelVerVtx == null ){ + String msg = " null modelVertex passed to getTopoElementForSvcOrResModelVer() at [" + trail + "]. "; + throw new AAIException("AAI_6114", msg); + } + + String modelVerId = modelVerVtx.<String>property("model-version-id").orElse(null); + if( modelVerId == null ){ + String nt = modelVerVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( nt != null && !nt.equals("model-ver") ){ + String msg = "Illegal model defined: model element pointing to nodeType: [" + + nt + "], should be pointing to: [model-ver] at [" + trail + "]. "; + throw new AAIException("AAI_6132", msg); + } + } + + Vertex firstElementVertex = null; + + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, modelVerVtx, "model-element"); + int elCount = 0; + while( vertI != null && vertI.hasNext() ){ + elCount++; + firstElementVertex = vertI.next(); + } + + if( elCount > 1 ){ + String msg = "Illegal model defined: More than one first element defined for model-ver-id = " + + modelVerId + " at [" + trail + "]. "; + throw new AAIException("AAI_6132", msg); + } + + if( firstElementVertex == null ){ + String msg = "Could not find first model element for model-ver-id = " + + modelVerId + " at [" + trail + "]. "; + throw new AAIException("AAI_6132", msg); + } + + return firstElementVertex; + + } // End of getTopElementForSvcOrResModelVer() + + + + /** + * Gets the named query prop over ride. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryElementVertex the named query element vertex + * @param instanceVertex the instance vertex + * @param apiVer the api ver + * @return HashMap of alternate properties to return for this element + * @throws AAIException the AAI exception + */ + public Map<String,Object> getNamedQueryPropOverRide( String transId, String fromAppId, + Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer ) + throws AAIException { + + // If this model-element says that they want an alternative set of properties returned, then pull that + // data out of the instance vertex. + + Map<String,Object> altPropHash = new HashMap<>(); + + if( namedQueryElementVertex == null ){ + String msg = " null namedQueryElementVertex passed to getNamedQueryPropOverRide() "; + throw new AAIException("AAI_6114", msg); + } + + List<String> propCollectList = new ArrayList<>(); + Iterator <VertexProperty<Object>> vpI = namedQueryElementVertex.properties("property-collect-list"); + while( vpI.hasNext() ){ + propCollectList.add((String)vpI.next().value()); + } + + for( int i = 0; i < propCollectList.size(); i++ ){ + String thisPropName = propCollectList.get(i); + Object instanceVal = instanceVertex.<Object>property(thisPropName).orElse(null); + altPropHash.put(thisPropName, instanceVal); + } + + return altPropHash; + + } // End of getNamedQueryPropOverRide() + + + /** + * Named query constraint says stop. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryElementVertex the named query element vertex + * @param instanceVertex the instance vertex + * @param apiVer the api ver + * @return true - if a constraint was defined that has not been met by the passed instanceVertex + * @throws AAIException the AAI exception + */ + public Boolean namedQueryConstraintSaysStop( String transId, String fromAppId, + Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer ) + throws AAIException { + + // For each (if any) property-constraint defined for this named-query-element, we will evaluate if + // the constraint is met or not-met. if there are constraints and any are not-met, then + // we return "true". + + if( namedQueryElementVertex == null ){ + String msg = " null namedQueryElementVertex passed to namedQueryConstraintSaysStop() "; + throw new AAIException("AAI_6114", msg); + } + if( instanceVertex == null ){ + String msg = " null instanceVertex passed to namedQueryConstraintSaysStop() "; + throw new AAIException("AAI_6114", msg); + } + + Iterator<Vertex> constrPipe = this.traverseIncidentEdges(EdgeType.TREE, namedQueryElementVertex, "property-constraint"); + if( constrPipe == null || !constrPipe.hasNext() ){ + // There's no "property-constraint" defined for this named-query-element. No problem. + return false; + } + + while( constrPipe.hasNext() ){ + Vertex constrVtx = (Vertex) constrPipe.next(); + // We found a property constraint that we will need to check + String conType = constrVtx.<String>property("constraint-type").orElse(null); + if( (conType == null) || conType.equals("")){ + String msg = " Bad property-constraint (constraint-type) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + String propName = constrVtx.<String>property("property-name").orElse(null); + if( (propName == null) || propName.equals("")){ + String msg = " Bad property-constraint (property-name) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + String propVal = constrVtx.<String>property("property-value").orElse(null); + if( (propVal == null) || propVal.equals("")){ + String msg = " Bad property-constraint (propVal) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + + // See if that constraint is met or not + String val = instanceVertex.<String>property(propName).orElse(null); + if( val == null ){ + val = ""; + } + + if( conType.equals("EQUALS") ){ + if( !val.equals(propVal) ){ + // This constraint was not met + return true; + } + } + else if( conType.equals("NOT-EQUALS") ){ + if( val.equals(propVal) ){ + // This constraint was not met + return true; + } + } + else { + String msg = " Bad property-constraint (constraint-type) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + } + + return false; + + } // End of namedQueryConstraintSaysStop() + + + /** + * Gets the named query extra data lookup. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryElementVertex the named query element vertex + * @param instanceVertex the instance vertex + * @param apiVer the api ver + * @return HashMap of alternate properties to return for this element + * @throws AAIException the AAI exception + */ + public Map<String,Object> getNamedQueryExtraDataLookup( String transId, String fromAppId, + Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer ) + throws AAIException { + + // For each (if any) related-lookup defined for this named-query-element, we will go and + // and try to find it. All the related-lookup data will get put in a hash and returned. + + if( namedQueryElementVertex == null ){ + String msg = " null namedQueryElementVertex passed to getNamedQueryExtraDataLookup() "; + throw new AAIException("AAI_6114", msg); + } + if( instanceVertex == null ){ + String msg = " null instanceVertex passed to getNamedQueryExtraDataLookup() "; + throw new AAIException("AAI_6114", msg); + } + + Map<String,Object> retHash = new HashMap<>(); + + Iterator<Vertex> lookPipe = this.traverseIncidentEdges(EdgeType.TREE, namedQueryElementVertex, "related-lookup"); + if( lookPipe == null || !lookPipe.hasNext() ){ + // There's no "related-lookup" defined for this named-query-element. No problem. + return retHash; + } + + while( lookPipe.hasNext() ){ + Vertex relLookupVtx = (Vertex) lookPipe.next(); + // We found a related-lookup record to try and use + String srcProp = relLookupVtx.<String>property("source-node-property").orElse(null); + if( (srcProp == null) || srcProp.equals("")){ + String msg = " Bad related-lookup (source-node-property) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + String targetNodeType = relLookupVtx.<String>property("target-node-type").orElse(null); + if( (targetNodeType == null) || targetNodeType.equals("")){ + String msg = " Bad related-lookup (targetNodeType) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + String targetProp = relLookupVtx.<String>property("target-node-property").orElse(null); + if( (targetProp == null) || targetProp.equals("")){ + String msg = " Bad related-lookup (target-node-property) found in Named Query definition. "; + throw new AAIException("AAI_6133", msg); + } + + List<String> propCollectList = new ArrayList<>(); + Iterator <VertexProperty<Object>> vpI = relLookupVtx.properties("property-collect-list"); + while( vpI.hasNext() ){ + propCollectList.add((String)vpI.next().value()); + } + + // Use the value from the source to see if we can find ONE target record using the value from the source + String valFromInstance = instanceVertex.<String>property(srcProp).orElse(null); + if( valFromInstance == null ){ + valFromInstance = ""; + } + + Map<String,Object> propHash = new HashMap<String,Object>(); + propHash.put(targetProp, valFromInstance); + + Optional<Vertex> result = dbMethHelper.locateUniqueVertex(targetNodeType, propHash); + if (!result.isPresent()) { + throw new AAIException("AAI_6114", "No Node of type " + targetNodeType + " found for properties"); + } + Vertex tmpVtx = result.get(); + // Pick up the properties from the target vertex that they wanted us to get + for( int j = 0; j < propCollectList.size(); j++ ){ + String tmpPropName = propCollectList.get(j); + Object valObj = tmpVtx.<Object>property(tmpPropName).orElse(null); + String lookupKey = targetNodeType + "." + tmpPropName; + retHash.put(lookupKey, valObj); + + } + } + + return retHash; + + } // End of getNamedQueryExtraDataLookup() + + /** + * Collect NQ element hash. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param thisLevelElemVtx the element verrtx for this level + * @param incomingTrail the incoming trail -- trail of nodeTypes that got us here (this nq-element vertex) from the top + * @param currentHash the current hash + * @param Map that got us to this point (that we will use as the base of the map we will return) + * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point + * @param levelCounter the level counter + * @return HashMap of all widget-points on a namedQuery topology with the value being the "named-query-element-uuid" for that spot. + * @throws AAIException the AAI exception + */ + public Map<String, String> collectNQElementHash( String transId, String fromAppId, + Vertex thisLevelElemVtx, String incomingTrail, + Map<String,String> currentHash, ArrayList <String> vidsTraversed, + int levelCounter ) throws AAIException { + + levelCounter++; + + Map<String, String> thisHash = new HashMap<>(); + thisHash.putAll(currentHash); + + if( levelCounter > MAX_LEVELS ) { + throw new AAIException("AAI_6125", "collectNQElementHash() has looped across more levels than allowed: " + MAX_LEVELS + ". "); + } + String thisGuysTrail = ""; + String thisElemVid = thisLevelElemVtx.id().toString(); + + // Find out what widget (and thereby what aai-node-type) this element represents. + String thisElementNodeType = getNqElementWidgetType( transId, fromAppId, thisLevelElemVtx, incomingTrail ); + + if( incomingTrail == null || incomingTrail.equals("") ){ + // This is the first one + thisGuysTrail = thisElementNodeType; + } + else { + thisGuysTrail = incomingTrail + "|" + thisElementNodeType; + } + vidsTraversed.add(thisElemVid); + + String nqElementUuid = thisLevelElemVtx.<String>property("named-query-element-uuid").orElse(null); + if( nqElementUuid == null || nqElementUuid.equals("") ){ + String msg = " named-query element UUID not found at trail = [" + incomingTrail + "]."; + throw new AAIException("AAI_6133", msg); + } + thisHash.put(thisGuysTrail, nqElementUuid ); + + // Now go "down" and look at the sub-elements pointed to so we can get their data. + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, thisLevelElemVtx, "named-query-element"); + while( vertI != null && vertI.hasNext() ){ + Vertex tmpVert = vertI.next(); + String vid = tmpVert.id().toString(); + Map<String,Object> elementHash = new HashMap<String, Object>(); + + String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( connectToType != null && connectToType.equals("named-query-element") ){ + // This is what we would expect + elementHash.put(vid, tmpVert); + } + else { + String msg = " named query element has [connectedTo] edge to improper nodeType= [" + + connectToType + "] trail = [" + incomingTrail + "]."; + throw new AAIException("AAI_6133", msg); + } + for( Map.Entry<String, Object> entry : elementHash.entrySet() ){ + Vertex elVert = (Vertex)(entry.getValue()); + String tmpElVid = elVert.id().toString(); + if( !vidsTraversed.contains(tmpElVid) ){ + // This is one we would like to use - so we'll recursively get it's result set to add to ours + Map<String, String> tmpHash = collectNQElementHash( transId, fromAppId, + elVert, thisGuysTrail, currentHash, vidsTraversed, levelCounter); + thisHash.putAll(tmpHash); + } + } + } + return thisHash; + + } // End of collectNQElementHash() + + + /** + * Collect delete key hash. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param thisLevelElemVtx the element vertex at this level + * @param incomingTrail the incoming trail -- trail of nodeTypes that got us here (this vertex) from the top + * @param currentHash the current hash + * @param Map that got us to this point (that we will use as the base of the map we will return) + * @param vidsTraversed the vids traversed ---- ArrayList of vertexId's that we traversed to get to this point + * @param levelCounter the level counter + * @param dbMaps the db maps + * @param modConstraintHash the mod constraint hash + * @param overRideModelId the over ride model id + * @param overRideModelVersionId the over ride model version id + * @return HashMap of all widget-points on a model topology with the value being the "newDataDelFlag" for that spot. + * @throws AAIException the AAI exception + */ + public Map<String, String> collectDeleteKeyHash( String transId, String fromAppId, + Vertex thisLevelElemVtx, String incomingTrail, + Map<String,String> currentHash, ArrayList <String> vidsTraversed, + int levelCounter, DbMaps dbMaps, Map<String, Vertex> modConstraintHash, + String overRideModelId, String overRideModelVersionId ) + throws AAIException { + + levelCounter++; + + Map<String, String> thisHash = new HashMap<>(); + thisHash.putAll(currentHash); + + if( levelCounter > MAX_LEVELS ) { + throw new AAIException("AAI_6125", "collectDeleteKeyHash() has looped across more levels than allowed: " + MAX_LEVELS + ". "); + } + String thisGuysTrail = ""; + String thisElemVid = thisLevelElemVtx.id().toString(); + Map<String, Vertex> modConstraintHash2Use = null; + + // If this element represents a resource or service model, then we will replace this element with + // the "top" element of that resource or service model. That model-element already points to its + // topology, so it will graft in that model's topology. + // EXCEPT - if this element has "linkage-points" defined, then we need to do some extra + // processing for how we join to that model and will not try to go any "deeper". + List<String> linkagePtList = new ArrayList<>(); + Iterator <VertexProperty<Object>> vpI = thisLevelElemVtx.properties("linkage-points"); + //DEBUG -- AAI-8002 + // I am not sure why, but since "linkage-points" is an xml-element-wrapper in the OXM definition, + // we get back the whole array of Strings in one String - but still use the "vtx.properties()" to + // get it - but only look at the first thing returned by the iterator. + if( vpI.hasNext() ){ + String tmpLinkageThing = (String)vpI.next().value(); + linkagePtList = makeSureItsAnArrayList( tmpLinkageThing ); + } + + if( linkagePtList != null && !linkagePtList.isEmpty() ){ + // Whatever this element is - we are connecting to it via a linkage-point + // We will figure out what to do and then return without going any deeper + String elemFlag = thisLevelElemVtx.<String>property("new-data-del-flag").orElse(null); + + Set<String> linkageConnectNodeTypes = getLinkageConnectNodeTypes( linkagePtList ); + Iterator <?> linkNtIter = linkageConnectNodeTypes.iterator(); + String incTrail = ""; + if( incomingTrail != null && !incomingTrail.equals("") ){ + incTrail = incomingTrail + "|"; + } + + while( linkNtIter.hasNext() ){ + // The 'trail' (or trails) for this element should just be the to the first-contact on the linkage point + String linkTrail = incTrail + linkNtIter.next(); + Boolean alreadyTaggedFalse = false; + if( thisHash.containsKey(linkTrail) && thisHash.get(linkTrail).equals("F") ){ + // some other path with a matching trail has the deleteFlag set to "F", so we do not want + // to override that since our model code only uses nodeTypes to know where it is - and we + // would rather do less deleting than needed instead of too much deleting. + alreadyTaggedFalse = true; + } + if( elemFlag != null && elemFlag.equals("T") && !alreadyTaggedFalse ){ + // This trail should be marked with an "T" + thisHash.put(linkTrail, "T"); + } + else { + thisHash.put(linkTrail, "F"); + } + } + return thisHash; + } + + // ---------------------------------------------------------------------------- + // If we got to here, then this was not an element that used a linkage-point + // ---------------------------------------------------------------------------- + + // Find out what widget-model (and thereby what aai-node-type) this element represents. + // Even if this element is pointing to a service or resource model, it must have a + // first element which is a single widget-type model. + String thisElementNodeType = getModElementWidgetType( thisLevelElemVtx, incomingTrail ); + String firstElementModelInfo = ""; + + vidsTraversed.add(thisElemVid); + Vertex elementVtxForThisLevel = null; + Vertex thisElementsModelVerVtx = getModelVerThatElementRepresents( thisLevelElemVtx, incomingTrail ); + Vertex thisElementsModelVtx = getModelGivenModelVer( thisElementsModelVerVtx, incomingTrail ); + String modType = getModelTypeFromModel( thisElementsModelVtx, incomingTrail ); + String subModelFirstModInvId = thisElementsModelVtx.<String>property("model-invariant-id").orElse(null); + String subModelFirstVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null); + if( modType.equals("widget") ){ + if( overRideModelId != null && !overRideModelId.equals("") ){ + // Note - this is just to catch the correct model for the TOP node in a model since + // it will have an element which will always be a widget even though the model + // could be a resource or service model. + firstElementModelInfo = "," + overRideModelId + "," + overRideModelVersionId; + } + } + else if( nodeTypeSupportsPersona(thisElementNodeType, dbMaps) ){ + firstElementModelInfo = "," + subModelFirstModInvId + "," + subModelFirstVerId; + } + + if( incomingTrail.equals("") ){ + // This is the first one + thisGuysTrail = thisElementNodeType + firstElementModelInfo; + } + else { + thisGuysTrail = incomingTrail + "|" + thisElementNodeType + firstElementModelInfo; + } + + String tmpFlag = "F"; + Boolean stoppedByASvcOrResourceModelElement = false; + if( modType.equals("widget") ){ + elementVtxForThisLevel = thisLevelElemVtx; + // For the element-model for the widget at this level, record it's delete flag + tmpFlag = elementVtxForThisLevel.<String>property("new-data-del-flag").orElse(null); + } + else { + // For an element that is referring to a resource or service model, we replace + // this element with the "top" element for that resource/service model so that the + // topology of that resource/service model will be included in this topology. + String modelVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null); + if( subModelFirstModInvId == null || subModelFirstModInvId.equals("") + || subModelFirstVerId == null || subModelFirstVerId.equals("") ){ + throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-invariant-id or model-version-id. Model-version-id = " + + modelVerId + ", at [" + incomingTrail + "]"); + } + + // BUT -- if the model-element HERE at the resource/service level does NOT have + // it's new-data-del-flag set to "T", then we do not need to go down into the + // sub-model looking for delete-able things. + + tmpFlag = thisLevelElemVtx.<String>property("new-data-del-flag").orElse(null); + elementVtxForThisLevel = getTopElementForSvcOrResModelVer(thisElementsModelVerVtx, thisGuysTrail); + if( tmpFlag != null && tmpFlag.equals("T") ){ + modConstraintHash2Use = getModConstraintHash( thisLevelElemVtx, modConstraintHash ); + } + else { + stoppedByASvcOrResourceModelElement = true; + } + // For the element-model for the widget at this level, record it's delete flag + tmpFlag = elementVtxForThisLevel.<String>property("new-data-del-flag").orElse(null); + } + + String flag2Use = "F"; // by default we'll use "F" for the delete flag + if( ! stoppedByASvcOrResourceModelElement ){ + // Since we haven't been stopped by a resource/service level "F", we can look at the lower level flag + if( thisHash.containsKey(thisGuysTrail) ){ + // We've seen this spot in the topology before - do not override the delete flag if the older one is "F" + // We will only over-ride it if the old one was "T" and the new one is "F" (anything but "T") + String oldFlag = thisHash.get(thisGuysTrail); + if( oldFlag.equals("T") && (tmpFlag != null) && tmpFlag.equals("T") ){ + // The old flag was "T" and the new flag is also "T" + flag2Use = "T"; + } + else { + // the old flag was not "F" - so don't override it + flag2Use = "F"; + } + } + else if( (tmpFlag != null) && tmpFlag.equals("T") ){ + // We have not seen this one, so we can set it to "T" if that's what it is. + flag2Use = "T"; + } + } + + thisHash.put(thisGuysTrail, flag2Use); + if( ! stoppedByASvcOrResourceModelElement ){ + // Since we haven't been stopped by a resource/service level "F", we will continue to + // go "down" and look at the elements pointed to so we can get their data. + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, elementVtxForThisLevel, "model-element", "constrained-element-set"); + while( vertI != null && vertI.hasNext() ){ + Vertex tmpVert = vertI.next(); + String vid = tmpVert.id().toString(); + Map<String,Object> elementHash = new HashMap<String, Object>(); + + String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( connectToType != null && connectToType.equals("model-element") ){ + // A nice, regular old model-element + elementHash.put(vid, tmpVert); + } + else if( (connectToType != null) && connectToType.equals("constrained-element-set") ){ + // translate the constrained-element-set into a hash of model-element Vertex's + String constrainedElementSetUuid = tmpVert.<String>property("constrained-element-set-uuid").orElse(null); + if( (modConstraintHash2Use != null) && modConstraintHash2Use.containsKey(constrainedElementSetUuid) ){ + // This constrained-element-set is being superseded by a different one + Vertex replacementConstraintVert = modConstraintHash.get(constrainedElementSetUuid); + elementHash = getNextStepElementsFromSet( replacementConstraintVert ); + // Now that we've found and used the replacement constraint, we don't need to carry it along any farther + modConstraintHash.remove(constrainedElementSetUuid); + } + else { + elementHash = getNextStepElementsFromSet( tmpVert ); + } + } + else { + String msg = " model-element has [connectedTo] edge to improper nodeType= [" + + connectToType + "] trail = [" + incomingTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + for( Map.Entry<String, Object> entry : elementHash.entrySet() ){ + Vertex elVert = (Vertex)(entry.getValue()); + String tmpElVid = elVert.id().toString(); + String tmpElNT = getModElementWidgetType( elVert, thisGuysTrail ); + check4EdgeRule(tmpElNT, thisElementNodeType, dbMaps); + if( !vidsTraversed.contains(tmpElVid) ){ + // This is one we would like to use - so we'll recursively get it's result set to add to ours + Map<String, String> tmpHash = collectDeleteKeyHash( transId, fromAppId, + elVert, thisGuysTrail, + currentHash, vidsTraversed, levelCounter, dbMaps, modConstraintHash2Use, + "", "" ); + thisHash.putAll(tmpHash); + } + } + } + } + return thisHash; + + } // End of collectDeleteKeyHash() + + + /** + * Gets the linkage connect node types. + * + * @param linkagePtList the linkage pt list + * @return the linkage connect node types + * @throws AAIException the AAI exception + */ + public Set<String> getLinkageConnectNodeTypes(List<String> linkagePtList ) + throws AAIException { + // linkage points are a path from the top of a model to where we link in. + // This method wants to just bring back a list of distinct last items. + // Ie: for the input with these two: "pserver|lag-link|l-interface" and "pserver|p-interface|l-interface" + // it would just return a single item, "l-interface" since both linkage points end in that same node-type. + + Set<String> linkPtSet = new HashSet<>(); + + if( linkagePtList == null ){ + String detail = " Bad (null) linkagePtList passed to getLinkageConnectNodeTypes() "; + throw new AAIException("AAI_6125", detail); + } + + for( int i = 0; i < linkagePtList.size(); i++ ){ + String [] trailSteps = linkagePtList.get(i).split("\\|"); + if( trailSteps == null || trailSteps.length == 0 ){ + String detail = " Bad incomingTrail passed to getLinkageConnectNodeTypes(): [" + linkagePtList + "] "; + throw new AAIException("AAI_6125", detail); + } + String lastStepNT = trailSteps[trailSteps.length - 1]; + linkPtSet.add(lastStepNT); + } + + return linkPtSet; + + }// End getLinkageConnectNodeTypes() + + + /** + * Collect topology for model-ver. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelElement vertex to collect for + * @param incomingTrail the incoming trail -- trail of nodeTypes/personaInfo that got us here (this vertex) from the top + * @param currentMap the current map -- map that got us to this point (that we will use as the base of the map we will return) + * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point + * @param levelCounter the level counter + * @param dbMaps the db maps + * @param modConstraintHash the mod constraint hash + * @param overRideModelInvId the override model-invariant-id + * @param overRideModelVersionId the override model-version-id + * @return Map of the topology + * @throws AAIException the AAI exception + */ + public Multimap<String, String> collectTopology4ModelVer( String transId, String fromAppId, + Vertex thisLevelElemVtx, String incomingTrail, + Multimap <String,String> currentMap, List<String> vidsTraversed, + int levelCounter, DbMaps dbMaps, Map<String, Vertex> modConstraintHash, + String overRideModelInvId, String overRideModelVersionId ) + throws AAIException { + + levelCounter++; + + Multimap <String, String> thisMap = ArrayListMultimap.create(); + thisMap.putAll(currentMap); + + if( levelCounter > MAX_LEVELS ) { + throw new AAIException("AAI_6125", "collectTopology4ModelVer() has looped across more levels than allowed: " + MAX_LEVELS + ". "); + } + String thisGuysTrail = ""; + String thisElemVid = thisLevelElemVtx.id().toString(); + Map<String, Vertex> modConstraintHash2Use = null; + + // If this element represents a resource or service model, then we will replace this element with + // the "top" element of that resource or service model. That model-element already points to its + // topology, so it will graft in that model's topology. + // EXCEPT - if this element defines "linkage-points" defined, then we need to do some extra + // processing for how we join to that model. + + // Find out what widget-model (and thereby what aai-node-type) this element represents. + // Even if this element is pointing to a service or resource model, it must have a + // first element which is a single widget-type model. + String firstElementModelInfo = ""; + String thisElementNodeType = getModElementWidgetType( thisLevelElemVtx, incomingTrail ); + if( nodeTypeSupportsPersona(thisElementNodeType, dbMaps) && overRideModelInvId != null && !overRideModelInvId.equals("") ){ + firstElementModelInfo = "," + overRideModelInvId + "," + overRideModelVersionId; + } + + Vertex elementVtxForThisLevel = null; + Vertex thisElementsModelVerVtx = getModelVerThatElementRepresents( thisLevelElemVtx, incomingTrail ); + String subModelFirstModInvId = ""; + String subModelFirstModVerId = ""; + String modInfo4Trail = ""; + String modType = getModelTypeFromModelVer( thisElementsModelVerVtx, incomingTrail ); + if( modType.equals("resource") || modType.equals("service") ){ + // For an element that is referring to a resource or service model, we replace this + // this element with the "top" element for that resource/service model so that the + // topology of that resource/service model gets included in this topology. + // -- Note - since that top element of a service or resource model will point to a widget model, + // we have to track what modelId/version it really maps so we can make our recursive call + Vertex thisElementsModelVtx = getModelGivenModelVer(thisElementsModelVerVtx, incomingTrail); + subModelFirstModInvId = thisElementsModelVtx.<String>property("model-invariant-id").orElse(null); + subModelFirstModVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null); + + if( nodeTypeSupportsPersona(thisElementNodeType, dbMaps) ){ + modInfo4Trail = "," + subModelFirstModInvId + "," + subModelFirstModVerId; + } + String modelVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null); + if( subModelFirstModInvId == null || subModelFirstModInvId.equals("") || subModelFirstModVerId == null || subModelFirstModVerId.equals("") ){ + throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-invariant-id or model-version-id. Model-ver-id = " + modelVerId); + } + + elementVtxForThisLevel = getTopElementForSvcOrResModelVer(thisElementsModelVerVtx, incomingTrail); + modConstraintHash2Use = getModConstraintHash( thisLevelElemVtx, modConstraintHash ); + } + else { + elementVtxForThisLevel = thisLevelElemVtx; + } + + if( incomingTrail.equals("") ){ + // This is the first one + thisGuysTrail = thisElementNodeType + firstElementModelInfo; + } + else { + thisGuysTrail = incomingTrail + "|" + thisElementNodeType + modInfo4Trail; + } + + // We only want to ensure that a particular element does not repeat on a single "branch". + // It could show up on other branches in the case where it is a sub-model which is being + // used in more than one place. + // + List<String> thisTrailsVidsTraversed = new ArrayList <String>(); + thisTrailsVidsTraversed.addAll(vidsTraversed); + thisTrailsVidsTraversed.add(thisElemVid); + + // Look at the elements pointed to at this level and add on their data + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, elementVtxForThisLevel, "model-element", "constrained-element-set"); + + while( vertI != null && vertI.hasNext() ){ + Vertex tmpVert = vertI.next(); + String vid = tmpVert.id().toString(); + Map<String,Object> elementHash = new HashMap<String, Object>(); + String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( connectToType != null && connectToType.equals("model-element") ){ + // A nice, regular old model-element + elementHash.put(vid, tmpVert); + } + else if( (connectToType != null) && connectToType.equals("constrained-element-set") ){ + // translate the constrained-element-set into a hash of model-element Vertex's + String constrainedElementSetUuid = tmpVert.<String>property("constrained-element-set-uuid").orElse(null); + if( (modConstraintHash2Use != null) && modConstraintHash2Use.containsKey(constrainedElementSetUuid) ){ + // This constrained-element-set is being superseded by a different one + Vertex replacementConstraintVert = modConstraintHash.get(constrainedElementSetUuid); + elementHash = getNextStepElementsFromSet( replacementConstraintVert ); + // Now that we've found and used the replacement constraint, we don't need to carry it along any farther + modConstraintHash.remove(constrainedElementSetUuid); + } + else { + elementHash = getNextStepElementsFromSet( tmpVert ); + } + } + else { + String msg = " model element has [connectedTo] edge to improper nodeType= [" + + connectToType + "] trail = [" + incomingTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + for( Map.Entry<String, Object> entry : elementHash.entrySet() ){ + Vertex elVert = (Vertex)(entry.getValue()); + String tmpElVid = elVert.id().toString(); + String tmpElNT = getModElementWidgetType( elVert, thisGuysTrail ); + String tmpElStepName = getModelElementStepName( elVert, thisGuysTrail, dbMaps ); + + List<String> linkagePtList = new ArrayList <String>(); + Iterator <VertexProperty<Object>> vpI = elVert.properties("linkage-points"); + //DEBUG -- AAI-8002 + // I am not sure why, but since "linkage-points" is an xml-element-wrapper in the OXM definition, + // we get back the whole array of Strings in one String - but still use the "vtx.properties()" to + // get it - but only look at the first thing returned by the iterator. + if( vpI.hasNext() ){ + String tmpLinkageThing = (String)vpI.next().value(); + linkagePtList = makeSureItsAnArrayList( tmpLinkageThing ); + } + + if( linkagePtList != null && !linkagePtList.isEmpty() ){ + // This is as far as we can go, we will use the linkage point info to define the + // rest of this "trail" + for( int i = 0; i < linkagePtList.size(); i++ ){ + Multimap<String, String> tmpMap = collectTopology4LinkagePoint( transId, fromAppId, + linkagePtList.get(i), thisGuysTrail, currentMap, dbMaps); + thisMap.putAll(tmpMap); + } + } + else { + check4EdgeRule(tmpElNT, thisElementNodeType, dbMaps); + thisMap.put(thisGuysTrail, tmpElStepName); + if( !thisTrailsVidsTraversed.contains(tmpElVid) ){ + // This is one we would like to use - so we'll recursively get it's result set to add to ours + Multimap<String, String> tmpMap = collectTopology4ModelVer( transId, fromAppId, + elVert, thisGuysTrail, + currentMap, thisTrailsVidsTraversed, levelCounter, + dbMaps, modConstraintHash2Use, subModelFirstModInvId, subModelFirstModVerId ); + thisMap.putAll(tmpMap); + } + else { + String modelElementUuid = elVert.<String>property("model-element-uuid").orElse(null); + String msg = "Bad Model Definition: looping model-element (model-element-uuid = [" + + modelElementUuid + "]) found trying to add step: [" + tmpElStepName + "], " + + " on trail = [" + thisGuysTrail + "]. "; + System.out.println( msg ); + throw new AAIException("AAI_6132", msg); + } + } + } + } + + return thisMap; + + } // End of collectTopology4ModelVer() + + + /** + * Check 4 edge rule. + * + * @param nodeTypeA the node type A + * @param nodeTypeB the node type B + * @param dbMaps the db maps + * @throws AAIException the AAI exception + */ + public void check4EdgeRule( String nodeTypeA, String nodeTypeB, DbMaps dbMaps ) throws AAIException { + // Throw an exception if there is no defined edge rule for this combination of nodeTypes in DbEdgeRules. + + final EdgeRules edgeRules = EdgeRules.getInstance(); + + if( !edgeRules.hasEdgeRule(nodeTypeA, nodeTypeB) + && !edgeRules.hasEdgeRule(nodeTypeB, nodeTypeA) ){ + // There's no EdgeRule for this -- find out if one of the nodeTypes is invalid or if + // they are valid, but there's just no edgeRule for them. + if( ! dbMaps.NodeProps.containsKey(nodeTypeA) ){ + String emsg = " Unrecognized nodeType aa [" + nodeTypeA + "]\n"; + throw new AAIException("AAI_6115", emsg); + } + else if( ! dbMaps.NodeProps.containsKey(nodeTypeB) ){ + String emsg = " Unrecognized nodeType bb [" + nodeTypeB + "]\n"; + throw new AAIException("AAI_6115", emsg); + } + else { + String msg = " No Edge Rule found for this pair of nodeTypes (order does not matter) [" + + nodeTypeA + "], [" + nodeTypeB + "]."; + throw new AAIException("AAI_6120", msg); + } + } + + } + + + /** + * Collect topology 4 linkage point. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param linkagePointStr -- Note it is in reverse order from where we connect to it. + * @param incomingTrail -- trail of nodeTypes that got us here (this vertex) from the top + * @param currentMap the current map -- that got us to this point (that we will use as the base of the map we will return) + * @param dbMaps the db maps + * @return Map of the topology + * @throws AAIException the AAI exception + */ + public Multimap<String, String> collectTopology4LinkagePoint( String transId, String fromAppId, + String linkagePointStrVal, String incomingTrail, Multimap <String,String> currentMap, DbMaps dbMaps ) + throws AAIException { + + Multimap <String, String> thisMap = ArrayListMultimap.create(); + thisMap.putAll(currentMap); + String thisGuysTrail = incomingTrail; + + // NOTE - "trails" can have multiple parts now since we track persona info for some. + // We just want to look at the node type info - which would be the piece + // before any commas (if there are any). + + String [] trailSteps = thisGuysTrail.split("\\|"); + if( trailSteps == null || trailSteps.length == 0 ){ + throw new AAIException("AAI_6125", "Bad incomingTrail passed to collectTopology4LinkagePoint(): [" + incomingTrail + "] "); + } + String lastStepString = trailSteps[trailSteps.length - 1]; + String [] stepPieces = lastStepString.split(","); + String lastStepNT = stepPieces[0]; + + // It is assumed that the linkagePoint string will be a pipe-delimited string where each + // piece is an AAIProperties.NODE_TYPE. For now, the first thing to connect to is what is on the farthest right. + // Example: linkagePoint = "pserver|p-interface|l-interface" would mean that we're connecting to the l-interface + // but that after that, we connect to a p-interface followed by a pserver. + // It might have been more clear to define it in the other direction, but for now, that is it. (16-07) + String linkagePointStr = linkagePointStrVal; + // --- DEBUG For AAI-8002 + // We are getting these with more than linkage thing in one string. + // Ie. "pserver|lag-interface|l-interface, pserver|p-interface|l-interface, vlan|l-interface" + linkagePointStr = linkagePointStr.replace("[", ""); + linkagePointStr = linkagePointStr.replace("]", ""); + linkagePointStr = linkagePointStr.replace(" ", ""); + + String [] linkage = linkagePointStr.split("\\,"); + for( int x = 0; x < linkage.length; x++ ){ + lastStepNT = stepPieces[0]; + String thisStepNT = ""; + String [] linkageSteps = linkage[x].split("\\|"); + if( linkageSteps == null || linkageSteps.length == 0 ){ + throw new AAIException("AAI_6125", "Bad linkagePointStr passed to collectTopology4LinkagePoint(): [" + linkagePointStr + "] "); + } + for( int i=(linkageSteps.length - 1); i >= 0; i-- ){ + thisStepNT = linkageSteps[i]; + check4EdgeRule(lastStepNT, thisStepNT, dbMaps); + thisMap.put(thisGuysTrail, thisStepNT); + thisGuysTrail = thisGuysTrail + "|" + thisStepNT; + lastStepNT = thisStepNT; + } + } + return thisMap; + + } // End of collectTopology4LinkagePoint() + + + /** + * Gets the next step elements from set. + * + * @param constrElemSetVtx the constr elem set vtx + * @return Hash of the set of model-elements this set represents + * @throws AAIException the AAI exception + */ + public Map<String,Object> getNextStepElementsFromSet( Vertex constrElemSetVtx ) + throws AAIException { + // Take a constrained-element-set and figure out the total set of all the possible elements that it + // represents and return them as a Hash. + + Map<String,Object> retElementHash = new HashMap<String, Object>(); + + if( constrElemSetVtx == null ){ + String msg = " getNextStepElementsFromSet() called with null constrElemSetVtx "; + throw new AAIException("AAI_6125", msg); + } + + String constrNodeType = constrElemSetVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + String constrElemSetUuid = constrElemSetVtx.<String>property("constrained-element-set-uuid").orElse(null); + if( constrNodeType == null || !constrNodeType.equals("constrained-element-set") ){ + String msg = " getNextStepElementsFromSet() called with wrong type model: [" + constrNodeType + "]. "; + throw new AAIException("AAI_6125", msg); + } + + ArrayList <Vertex> choiceSetVertArray = new ArrayList<Vertex>(); + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, constrElemSetVtx, "element-choice-set"); + int setCount = 0; + while( vertI != null && vertI.hasNext() ){ + Vertex choiceSetVertex = vertI.next(); + String constrSetType = choiceSetVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( constrSetType != null && constrSetType.equals("element-choice-set") ){ + choiceSetVertArray.add(choiceSetVertex); + setCount++; + } + } + + if( setCount == 0 ){ + String msg = "No element-choice-set found under constrained-element-set-uuid = " + constrElemSetUuid; + throw new AAIException("AAI_6132", msg); + } + + // Loop through each choice-set and grab the model-elements + for( int i = 0; i < setCount; i++ ){ + Vertex choiceSetVert = choiceSetVertArray.get(i); + Iterator<Vertex> mVertI = this.traverseIncidentEdges(EdgeType.TREE, choiceSetVert, "model-element"); + int elCount = 0; + while( mVertI != null && mVertI.hasNext() ){ + Vertex tmpElVertex = mVertI.next(); + String elNodeType = tmpElVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( elNodeType != null && elNodeType.equals("model-element") ){ + String tmpVid = tmpElVertex.id().toString(); + retElementHash.put(tmpVid, tmpElVertex); + elCount++; + } + else { + // unsupported node type found for this choice-set + String msg = "Unsupported nodeType (" + elNodeType + + ") found under choice-set under constrained-element-set-uuid = " + constrElemSetUuid; + throw new AAIException("AAI_6132", msg); + } + } + + if( elCount == 0 ){ + String msg = "No model-elements found in choice-set under constrained-element-set-uuid = " + constrElemSetUuid; + throw new AAIException("AAI_6132", msg); + } + + } + return retElementHash; + + } // End of getNextStepElementsFromSet() + + + + /** + * Gen topo map 4 named Q. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param queryVertex the query vertex + * @param namedQueryUuid the named query uuid + * @return MultiMap of valid next steps for each potential query-element + * @throws AAIException the AAI exception + */ + public Multimap<String, String> genTopoMap4NamedQ( String transId, String fromAppId, + Vertex queryVertex, String namedQueryUuid ) + throws AAIException { + + if( queryVertex == null ){ + throw new AAIException("AAI_6125", "null queryVertex passed to genTopoMap4NamedQ()"); + } + + Multimap <String, String> initialEmptyMap = ArrayListMultimap.create(); + List<String> vidsTraversed = new ArrayList<>(); + + Vertex firstElementVertex = null; + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, queryVertex, "named-query-element"); + int elCount = 0; + while( vertI != null && vertI.hasNext() ){ + elCount++; + firstElementVertex = vertI.next(); + } + + if( elCount > 1 ){ + throw new AAIException("AAI_6133", "Illegal query defined: More than one first element defined for = " + namedQueryUuid); + } + + if( firstElementVertex == null ){ + throw new AAIException("AAI_6114", "Could not find first query element = " + namedQueryUuid); + } + + Vertex modVtx = getModelThatNqElementRepresents( firstElementVertex, "" ); + String modelType = getModelTypeFromModel( modVtx, "" ); + if( ! modelType.equals("widget") ){ + throw new AAIException("AAI_6133", "Bad Named Query Definition: First element must correspond to a widget type model. Named Query UUID = " + + namedQueryUuid); + } + + Multimap <String, String> collectedMap = collectTopology4NamedQ( transId, fromAppId, + firstElementVertex, "", + initialEmptyMap, vidsTraversed, 0); + + return collectedMap; + + } // End of genTopoMap4NamedQ() + + + + /** + * Collect topology 4 named Q. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param thisLevelElemVtx the model element vertex for this level + * @param levelCounter the level counter + * @return resultSet + * @throws AAIException the AAI exception + */ + public Multimap<String, String> collectTopology4NamedQ( String transId, String fromAppId, + Vertex thisLevelElemVtx, String incomingTrail, + Multimap <String,String> currentMap, List<String> vidsTraversed, int levelCounter ) + throws AAIException { + + levelCounter++; + + Multimap <String, String> thisMap = ArrayListMultimap.create(); + thisMap.putAll(currentMap); + + String thisElemVid = thisLevelElemVtx.id().toString(); + if( levelCounter > MAX_LEVELS ) { + throw new AAIException("AAI_6125", "collectModelStructure() has looped across more levels than allowed: " + MAX_LEVELS + ". "); + } + String thisGuysTrail = ""; + + // find out what widget (and thereby what aai-node-type) this element represents + String thisElementNodeType = getNqElementWidgetType( transId, fromAppId, thisLevelElemVtx, incomingTrail ); + + if( incomingTrail.equals("") ){ + // This is the first one + thisGuysTrail = thisElementNodeType; + } + else { + thisGuysTrail = incomingTrail + "|" + thisElementNodeType; + } + + vidsTraversed.add(thisElemVid); + + // Look at the elements pointed to at this level and add on their data + Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, thisLevelElemVtx, "named-query-element"); + while( vertI != null && vertI.hasNext() ){ + Vertex tmpVert = vertI.next(); + String tmpVid = tmpVert.id().toString(); + String tmpElNT = getNqElementWidgetType( transId, fromAppId, tmpVert, thisGuysTrail ); + thisMap.put(thisGuysTrail, tmpElNT); + if( !vidsTraversed.contains(tmpVid) ){ + // This is one we would like to use - so we'll recursively get it's result set to add to ours + Multimap<String, String> tmpMap = collectTopology4NamedQ( transId, fromAppId, + tmpVert, thisGuysTrail, + currentMap, vidsTraversed, levelCounter); + thisMap.putAll(tmpMap); + } + } + + return thisMap; + + } // End of collectTopology4NamedQ() + + + /** + * Gets the model that NamedQuery element represents. + * + * @param elementVtx the NQ element vtx + * @param elementTrail the element trail + * @return the model that element represents + * @throws AAIException the AAI exception + */ + public Vertex getModelThatNqElementRepresents( Vertex elementVtx, String elementTrail ) + throws AAIException { + + // Get the model that a named-query element represents + Vertex modVtx = null; + Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.COUSIN, elementVtx, "model"); + int modCount = 0; + while( mvertI != null && mvertI.hasNext() ){ + modCount++; + modVtx = mvertI.next(); + } + + if( modCount > 1 ){ + String msg = "Illegal element defined: More than one model pointed to by a single named-query-element at [" + + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + if( modVtx == null ){ + String msg = "Bad named-query definition: Could not find model for element. "; + if( !elementTrail.equals("") ){ + msg = "Bad named-query definition: Could not find model for named-query-element at [" + elementTrail + "]."; + } + throw new AAIException("AAI_6132", msg); + } + + String nodeType = modVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( (nodeType != null) && nodeType.equals("model") ){ + return modVtx; + } + else { + String msg = "Illegal Named Query element defined: expecting a 'model', but found 'isA' edge pointing to nodeType = " + + nodeType + "] at [" + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + }// getModelThatNqElementRepresents() + + + /** + * Gets the model-ver that element represents. + * + * @param elementVtx the element vtx + * @param elementTrail the element trail + * @return the model-ver that element represents + * @throws AAIException the AAI exception + */ + public Vertex getModelVerThatElementRepresents( Vertex elementVtx, String elementTrail ) + throws AAIException { + + // Get the model-ver that an element represents + Vertex modVerVtx = null; + Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.COUSIN, elementVtx, "model-ver"); + int modCount = 0; + while( mvertI != null && mvertI.hasNext() ){ + modCount++; + modVerVtx = mvertI.next(); + } + + if( modCount > 1 ){ + String msg = "Illegal element defined: More than one model pointed to by a single element at [" + + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + if( modVerVtx == null ){ + String msg = "Bad model definition: Could not find model-ver for model-element. "; + if( !elementTrail.equals("") ){ + msg = "Bad model definition: Could not find model-VER for model-element at [" + elementTrail + "]."; + } + throw new AAIException("AAI_6132", msg); + } + + String nodeType = modVerVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if( (nodeType != null) && nodeType.equals("model-ver") ){ + return modVerVtx; + } + else { + String msg = "Illegal model-element defined: expecting a 'model-ver', but found 'isA' edge pointing to nodeType = " + + nodeType + "] at [" + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + }// getModelVerThatElementRepresents() + + + + /** + * Gets the model that is parent to model-ver node. + * + * @param modVerVtx the model-ver vtx + * @param elementTrail the element trail + * @return the model that element represents + * @throws AAIException the AAI exception + */ + public Vertex getModelGivenModelVer( Vertex modVerVtx, String elementTrail ) + throws AAIException { + + // Get the parent model for this "model-ver" node + Vertex modVtx = null; + Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.TREE, modVerVtx, "model"); + int modCount = 0; + while( mvertI != null && mvertI.hasNext() ){ + modCount++; + modVtx = mvertI.next(); + } + + if( modCount > 1 ){ + String msg = "Illegal model-ver node defined: More than one model points to it with a 'has' edge [" + + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + if( modVtx == null ){ + String msg = "Bad model-ver node: Could not find parent model. "; + if( !elementTrail.equals("") ){ + msg = "Bad model-ver node: Could not find parent model. [" + elementTrail + "]."; + } + throw new AAIException("AAI_6132", msg); + } + + String nodeType = modVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);; + if( (nodeType != null) && nodeType.equals("model") ){ + // Found what we were looking for. + return modVtx; + } + else { + // Something is amiss + String msg = " Could not find parent model node for model-ver node at [" + + elementTrail + "]."; + throw new AAIException("AAI_6125", msg); + } + + + }// getModelGivenModelVer() + + + + /** + * Gets the model type. + * + * @param modelVtx the model vtx + * @param elementTrail the element trail + * @return the model type + * @throws AAIException the AAI exception + */ + public String getModelTypeFromModel( Vertex modelVtx, String elementTrail ) + throws AAIException { + + // Get the model-type from a model vertex + if( modelVtx == null ){ + String msg = " null modelVtx passed to getModelTypeFromModel() "; + throw new AAIException("AAI_6114", msg); + } + + String modelType = modelVtx.<String>property("model-type").orElse(null); + if( (modelType == null) || modelType.equals("") ){ + String msg = "Could not find model-type for model encountered at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + if( !modelType.equals("widget") && !modelType.equals("resource") && !modelType.equals("service") ){ + String msg = "Unrecognized model-type, [" + modelType + "] for model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + return modelType; + + }// getModelTypeFromModel() + + + + /** + * Gets the model type given model-ver + * + * @param modelVerVtx the model-ver vtx + * @param elementTrail the element trail + * @return the model type + * @throws AAIException the AAI exception + */ + public String getModelTypeFromModelVer( Vertex modelVerVtx, String elementTrail ) + throws AAIException { + + // Get the model-type given a model-ver vertex + if( modelVerVtx == null ){ + String msg = " null modelVerVtx passed to getModelTypeFromModelVer() "; + throw new AAIException("AAI_6114", msg); + } + + Vertex modVtx = getModelGivenModelVer( modelVerVtx, elementTrail ); + String modelType = modVtx.<String>property("model-type").orElse(null); + if( (modelType == null) || modelType.equals("") ){ + String msg = "Could not find model-type for model encountered at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + if( !modelType.equals("widget") && !modelType.equals("resource") && !modelType.equals("service") ){ + String msg = "Unrecognized model-type, [" + modelType + "] for model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + return modelType; + + }// getModelTypeFromModelVer() + + + + /** + * Gets the model-element step name. + * + * @param elementVtx the model-element vtx + * @param elementTrail the element trail + * @param dbMaps the db maps + * @return the element step name + * @throws AAIException the AAI exception + */ + public String getModelElementStepName( Vertex elementVtx, String elementTrail, DbMaps dbMaps) + throws AAIException { + + // Get the "step name" for a model-element + // Step names look like this for widget-models: AAIProperties.NODE_TYPE + // Step names look like this for resource/service models: "aai-node-type,model-invariant-id,model-version-id" + // NOTE -- if the element points to a resource or service model, then we'll return the + // widget-type of the first element (crown widget) for that model. + String thisElementNodeType = "?"; + Vertex modVerVtx = getModelVerThatElementRepresents( elementVtx, elementTrail ); + String modelType = getModelTypeFromModelVer( modVerVtx, elementTrail ); + + if( modelType == null ){ + String msg = " could not determine modelType in getModelElementStepName(). elementTrail = [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + if( modelType.equals("widget") ){ + // NOTE: for models that have model-type = "widget", their "model-name" maps directly to aai-node-type + thisElementNodeType = modVerVtx.<String>property("model-name").orElse(null); + if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){ + String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + return thisElementNodeType; + } + else if( modelType.equals("resource") || modelType.equals("service") ){ + Vertex modVtx = getModelGivenModelVer( modVerVtx, elementTrail ); + String modInvId = modVtx.<String>property("model-invariant-id").orElse(null); + String modVerId = modVerVtx.<String>property("model-version-id").orElse(null); + Vertex relatedTopElementModelVtx = getTopElementForSvcOrResModelVer( modVerVtx, elementTrail ); + Vertex relatedModelVtx = getModelVerThatElementRepresents( relatedTopElementModelVtx, elementTrail ); + thisElementNodeType = relatedModelVtx.<String>property("model-name").orElse(null); + + if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){ + String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + String stepName = ""; + if( nodeTypeSupportsPersona(thisElementNodeType, dbMaps) ){ + // This nodeType that this resource or service model refers to does support persona-related fields, so + // we will use model-invariant-id and model-version-id as part of the step name. + stepName = thisElementNodeType + "," + modInvId + "," + modVerId; + } + else { + stepName = thisElementNodeType; + } + return stepName; + } + else { + String msg = " Unrecognized model-type = [" + modelType + "] pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + }// getModelElementStepName() + + + + /** + * Node type supports persona. + * + * @param nodeType the node type + * @param dbMaps the db maps + * @return the boolean + * @throws AAIException the AAI exception + */ + public Boolean nodeTypeSupportsPersona(String nodeType, DbMaps dbMaps) + throws AAIException { + + if( nodeType == null || nodeType.equals("") ){ + return false; + } + + // Return true if this type of node supports the properties: "model-invariant-id-local" and "model-version-id-local" + if( ! dbMaps.NodeProps.containsKey(nodeType) ){ + String emsg = " Unrecognized nodeType [" + nodeType + "]\n"; + throw new AAIException("AAI_6115", emsg); + } + + Collection <String> props4ThisNT = dbMaps.NodeProps.get(nodeType); + if( !props4ThisNT.contains("model-invariant-id-local") || !props4ThisNT.contains("model-version-id-local") ){ + return false; + } + else { + return true; + } + + }// nodeTypeSupportsPersona() + + + /** + * Gets a Named Query element's widget type. + * + * @param elementVtx the named-query element vtx + * @param elementTrail the element trail + * @return the element widget type + * @throws AAIException the AAI exception + */ + public String getNqElementWidgetType( String transId, String fromAppId, + Vertex elementVtx, String elementTrail ) + throws AAIException { + + String thisNqElementWidgetType = ""; + // Get the associated node-type for the model pointed to by a named-query-element. + // NOTE -- if the element points to a resource or service model, then we'll return the + // widget-type of the first element (crown widget) for that model. + Vertex modVtx = getModelThatNqElementRepresents( elementVtx, elementTrail ); + String modelType = getModelTypeFromModel( modVtx, elementTrail ); + + if( modelType == null || !modelType.equals("widget") ){ + String emsg = " Model Type must be 'widget' for NamedQuery elements. Found [" + modelType + "] at [" + + elementTrail + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + else { + // For a Widget model, the nodeType is just mapped to the model-element.model-name + List<Vertex> modVerVtxArr = getModVersUsingModel(transId, fromAppId, modVtx); + if( modVerVtxArr != null && !modVerVtxArr.isEmpty() ){ + thisNqElementWidgetType = (modVerVtxArr.get(0)).<String>property("model-name").orElse(null); + } + if( thisNqElementWidgetType == null || thisNqElementWidgetType.equals("") ){ + String emsg = " Widget type could not be determined at [" + elementTrail + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + else { + return thisNqElementWidgetType; + } + } + + + }// End getNqElementWidgetType() + + + /** + * Gets a model-element's top widget type. + * + * @param elementVtx the model element vtx + * @param elementTrail the element trail + * @return the element widget type + * @throws AAIException the AAI exception + */ + public String getModElementWidgetType( Vertex elementVtx, String elementTrail ) + throws AAIException { + + // Get the associated node-type for the model-ver pointed to by a model-element. + // NOTE -- if the element points to a resource or service model, then we'll return the + // widget-type of the first element (crown widget) for that model. + Vertex modVerVtx = getModelVerThatElementRepresents( elementVtx, elementTrail ); + String thisElementNodeType = getModelVerTopWidgetType( modVerVtx, elementTrail ); + return thisElementNodeType; + + }// End getModElementWidgetType() + + + /** + * Gets the node using unique identifier + * + * @param transId the trans id + * @param fromAppId the from app id + * @param nodeType the nodeType + * @param idPropertyName the property name of the unique identifier + * @param uniqueIdVal the UUID value + * @return unique vertex found using UUID + * @throws AAIException the AAI exception + */ + public Vertex getNodeUsingUniqueId( String transId, String fromAppId, + String nodeType, String idPropertyName, String uniqueIdVal ) + throws AAIException { + + // Given a unique identifier, get the Vertex + if( uniqueIdVal == null || uniqueIdVal.equals("") ){ + String emsg = " Bad uniqueIdVal passed to getNodeUsingUniqueId(): [" + + uniqueIdVal + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + if( idPropertyName == null || idPropertyName.equals("") ){ + String emsg = " Bad idPropertyName passed to getNodeUsingUniqueId(): [" + + idPropertyName + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + if( nodeType == null || nodeType.equals("") ){ + String emsg = " Bad nodeType passed to getNodeUsingUniqueId(): [" + + nodeType + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + Vertex uniqVtx = null; + Iterable <?> uniqVerts = null; + uniqVerts = engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,nodeType).has(idPropertyName,uniqueIdVal).toList(); + if( uniqVerts == null ){ + String emsg = "Node could not be found for nodeType = [" + nodeType + + "], propertyName = [" + idPropertyName + + "], propertyValue = [" + uniqueIdVal + "]\n"; + throw new AAIException("AAI_6114", emsg); + } + else { + int count = 0; + Iterator <?> uniqVertsIter = uniqVerts.iterator(); + if( !uniqVertsIter.hasNext() ){ + String emsg = "Node could not be found for nodeType = [" + nodeType + + "], propertyName = [" + idPropertyName + + "], propertyValue = [" + uniqueIdVal + "]\n"; + throw new AAIException("AAI_6114", emsg); + } + else { + while( uniqVertsIter.hasNext() ){ + count++; + uniqVtx = (Vertex) uniqVertsIter.next(); + if( count > 1 ){ + String emsg = "More than one node found for nodeType = [" + nodeType + + "], propertyName = [" + idPropertyName + + "], propertyValue = [" + uniqueIdVal + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + } + } + } + + return uniqVtx; + }// End getNodeUsingUniqueId() + + + /** + * Gets the model-ver nodes using name. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelName the model name + * @return the model-ver's that use this name + * @throws AAIException the AAI exception + */ + public List<Vertex> getModelVersUsingName( String transId, String fromAppId, + String modelName ) + throws AAIException { + + // Given a "model-name", find the model-ver vertices that this maps to + if( modelName == null || modelName.equals("") ){ + String emsg = " Bad modelName passed to getModelVersUsingName(): [" + + modelName + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + List<Vertex> retVtxArr = new ArrayList<>(); + Iterator<Vertex> modVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName); + if( !modVertsIter.hasNext() ){ + String emsg = "Model-ver record(s) could not be found for model-ver data passed. model-name = [" + + modelName + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + else { + while( modVertsIter.hasNext() ){ + Vertex tmpModelVerVtx = (Vertex) modVertsIter.next(); + retVtxArr.add(tmpModelVerVtx); + } + } + + return retVtxArr; + + }// End getModelVersUsingName() + + + /** + * Gets the model-ver nodes using model-invariant-id. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param model-invariant-id (uniquely identifies a model) + * @return the model-ver's defined for the corresponding model + * @throws AAIException the AAI exception + */ + public Iterator<Vertex> getModVersUsingModelInvId( String transId, String fromAppId, + String modelInvId ) + throws AAIException { + + // Given a "model-invariant-id", find the model-ver nodes that this maps to + if( modelInvId == null || modelInvId.equals("") ){ + String emsg = " Bad model-invariant-id passed to getModVersUsingModelInvId(): [" + + modelInvId + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + Vertex modVtx = getNodeUsingUniqueId(transId, fromAppId, "model", "model-invariant-id", modelInvId); + List<Vertex> retVtxArr = getModVersUsingModel(transId, fromAppId, modVtx); + if( retVtxArr == null || retVtxArr.isEmpty() ){ + String emsg = " Model-ver record(s) could not be found attached to model with model-invariant-id = [" + + modelInvId + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + + return retVtxArr.iterator(); + }// End getModVersUsingModelInvId() + + + /** + * Gets the model-ver nodes using a model node. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param model vertex + * @return the model-ver's defined for the corresponding model + * @throws AAIException the AAI exception + */ + public List<Vertex> getModVersUsingModel( String transId, String fromAppId, + Vertex modVtx ) + throws AAIException { + + if( modVtx == null ){ + String emsg = " Null model vertex passed to getModVersUsingModel(): "; + throw new AAIException("AAI_6118", emsg); + } + + List<Vertex> retVtxArr = new ArrayList<>(); + Iterator<Vertex> modVerVertsIter = this.traverseIncidentEdges(EdgeType.TREE, modVtx, "model-ver"); + if(!modVerVertsIter.hasNext()){ + String modelInvId = modVtx.<String>property("model-invariant-id").orElse(null); + String emsg = "Model-ver record(s) could not be found attached to model with model-invariant-id = [" + + modelInvId + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + else { + while( modVerVertsIter.hasNext() ){ + Vertex tmpModelVtx = (Vertex) modVerVertsIter.next(); + retVtxArr.add(tmpModelVtx); + } + } + + return retVtxArr; + + }// End getModVersUsingModel() + + /** + * Gets the model-version-ids using model-name. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelName the model name + * @return the model uuids using name + * @throws AAIException the AAI exception + */ + public List<String> getModelVerIdsUsingName( String transId, String fromAppId, + String modelName ) + throws AAIException { + + // Given a model-name find the model-ver nodes that it maps to + if( modelName == null || modelName.equals("") ){ + String emsg = " Bad modelName passed to getModelVerIdsUsingName(): [" + + modelName + "]\n"; + throw new AAIException("AAI_6118", emsg); + } + + List<String> retArr = new ArrayList<>(); + Iterator<Vertex> modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName); + if( !modVerVertsIter.hasNext() ){ + String emsg = " model-ver record(s) could not be found for model data passed. model-name = [" + + modelName + "]\n"; + throw new AAIException("AAI_6114", emsg); + } + else { + while( modVerVertsIter.hasNext() ){ + Vertex modelVerVtx = (Vertex) modVerVertsIter.next(); + String tmpUuid = modelVerVtx.<String>property("model-version-id").orElse(null); + if( (tmpUuid != null) && !tmpUuid.equals("") && !retArr.contains(tmpUuid) ){ + retArr.add(tmpUuid); + } + } + } + + if( retArr.isEmpty() ){ + String emsg = "No model-ver record found for model-name = [" + + modelName + "]\n"; + throw new AAIException("AAI_6132", emsg); + } + + return retArr; + }// End getModelVerIdsUsingName() + + + /** + * Gets the model top widget type. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelVersionId the model-version-id + * @param modelInvId the model-invariant-id + * @param modelName the model-name + * @return the model top widget type + * @throws AAIException the AAI exception + */ + public String getModelVerTopWidgetType( String transId, String fromAppId, + String modelVersionId, String modelInvId, String modelName ) + throws AAIException { + + // Could be given a model-ver's key info (model-version-id), OR, just a (non-unique) model-name, + // Or just a model-invariant-id (which could have multiple model-ver records under it). + // In any case, they should only map to one single "top" node-type for the first element. + + String nodeType = "?"; + Iterator<Vertex> modVerVertsIter; + + if( modelVersionId != null && !modelVersionId.equals("") ){ + // this would be the best - we can just look up the model-ver records directly + modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-version-id",modelVersionId); + } + else if( modelName != null && !modelName.equals("") ){ + modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName); + } + else if( modelInvId != null && !modelInvId.equals("") ){ + modVerVertsIter = getModVersUsingModelInvId(transId, fromAppId, modelInvId); + } + else { + String msg = "Neither modelVersionId, modelInvariantId, nor modelName passed to: getModelVerTopWidgetType() "; + throw new AAIException("AAI_6120", msg); + } + + if( !modVerVertsIter.hasNext() ){ + String emsg = "model-ver record(s) could not be found for model data passed: modelInvariantId = [" + modelInvId + + "], modeVersionId = [" + modelVersionId + "], modelName = [" + modelName + "]\n"; + throw new AAIException("AAI_6114", emsg); + } + else { + String lastNT = ""; + if( !modVerVertsIter.hasNext() ){ + String emsg = "model-ver record(s) could not be found for model data passed: modelInvariantId = [" + modelInvId + + "], modeVersionId = [" + modelVersionId + "], modelName = [" + modelName + "]\n"; + throw new AAIException("AAI_6114", emsg); + } + while( modVerVertsIter.hasNext() ){ + Vertex tmpModVerVtx = (Vertex) modVerVertsIter.next(); + String tmpNT = getModelVerTopWidgetType( tmpModVerVtx, "" ); + if( lastNT != null && !lastNT.equals("") ){ + if( !lastNT.equals(tmpNT) ){ + String emsg = "Different top-node-types (" + tmpNT + ", " + lastNT + + ") found for model data passed. (" + + " modelVersionId = [" + modelVersionId + + "], modelId = [" + modelInvId + + "], modelName = [" + modelName + + "])\n"; + throw new AAIException("AAI_6114", emsg); + } + } + lastNT = tmpNT; + nodeType = tmpNT; + } + } + + return nodeType; + + }// End getModelVerTopWidgetType() + + + /** + * Gets the widget type that this model-ver starts with. + * + * @param modVerVtx the model-version vtx + * @param elementTrail the element trail + * @return the widget type of the starting node of this model + * @throws AAIException the AAI exception + */ + public String getModelVerTopWidgetType( Vertex modVerVtx, String elementTrail ) + throws AAIException { + // Get the associated nodeType (Ie. aai-node-type / widget-type) for a model-ver. + // NOTE -- if the element points to a resource or service model, then we'll return the + // widget-type of the first element (crown widget) for that model. + String modelType = getModelTypeFromModelVer( modVerVtx, elementTrail ); + if( modelType == null ){ + String msg = " Could not determine modelType in getModelVerTopWidgetType(). elementTrail = [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + String thisElementNodeType = "?"; + if( modelType.equals("widget") ){ + // NOTE: for models that have model-type = "widget", their child model-ver nodes will + // have "model-name" which maps directly to aai-node-type (all model-ver's under one + // model should start with the same widget-type, so we only need to look at one). + thisElementNodeType = modVerVtx.<String>property("model-name").orElse(null); + if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){ + String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + } + else if( modelType.equals("resource") || modelType.equals("service") ){ + Vertex relatedTopElementVtx = getTopElementForSvcOrResModelVer( modVerVtx, elementTrail ); + Vertex relatedModVerVtx = getModelVerThatElementRepresents( relatedTopElementVtx, elementTrail ); + thisElementNodeType = relatedModVerVtx.<String>property("model-name").orElse(null); + if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){ + String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + } + else { + String msg = " Unrecognized model-type = [" + modelType + "] pointed to by element at [" + elementTrail + "]."; + throw new AAIException("AAI_6132", msg); + } + + return thisElementNodeType; + + }// getModelVerTopWidgetType() + + + /** + * Validate model. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param modelNameVersionId the model name version id + * @param apiVersion the api version + * @throws AAIException the AAI exception + */ + public void validateModel(String transId, String fromAppId, String modelVersionIdVal, String apiVersion ) + throws AAIException{ + + // Note - this will throw an exception if the model either can't be found, or if + // we can't figure out its topology map. + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + Vertex modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", + "model-version-id", modelVersionIdVal); + if( modelVerVtx == null ){ + String msg = " Could not find model-ver with modelVersionId = [" + modelVersionIdVal + "]."; + throw new AAIException("AAI_6114", msg); + } + else { + Multimap<String, String> topoMap = genTopoMap4ModelVer( transId, fromAppId, + modelVerVtx, modelVersionIdVal, dbMaps ); + String msg = " modelVer [" + modelVersionIdVal + "] topo multiMap looks like: \n[" + topoMap + "]"; + System.out.println("INFO -- " + msg ); + } + return; + + }// End validateModel() + + + /** + * Validate named query. + * + * @param transId the trans id + * @param fromAppId the from app id + * @param namedQueryUuid the named query uuid + * @param apiVersion the api version + * @throws AAIException the AAI exception + */ + public void validateNamedQuery(String transId, String fromAppId, String namedQueryUuid, String apiVersion ) + throws AAIException{ + + // Note - this will throw an exception if the named query either can't be found, or if + // we can't figure out its topology map. + Vertex nqVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query", + "named-query-uuid", namedQueryUuid); + + if( nqVtx == null ){ + String msg = " Could not find named-query with namedQueryUuid = [" + namedQueryUuid + "]."; + throw new AAIException("AAI_6114", msg); + } + else { + //Multimap<String, String> topoMap = genTopoMap4NamedQ( "junkTransId", "junkFromAppId", + // graph, nqVtx, namedQueryUuid ); + //System.out.println("DEBUG -- for test only : --- "); + //System.out.println("DEBUG -- topomap = [" + topoMap + "]"); + } + return; + + }// End validateNamedQuery() + + + /** + * Show result set. + * + * @param resSet the res set + * @param levelCount the level count + */ + public void showResultSet( ResultSet resSet, int levelCount ) { + + levelCount++; + String propsStr = ""; + for( int i= 1; i <= levelCount; i++ ){ + propsStr = propsStr + "-"; + } + if( resSet.getVert() == null ){ + return; + } + String nt = resSet.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null); + propsStr = propsStr + "[" + nt + "] "; + + //propsStr = propsStr + " newDataDelFlag = " + resSet.getNewDataDelFlag() + ", trail = " + resSet.getLocationInModelSubGraph(); + //propsStr = propsStr + "limitDesc = [" + resSet.getPropertyLimitDesc() + "]"; + propsStr = propsStr + " trail = " + resSet.getLocationInModelSubGraph(); + + Map<String,Object> overrideHash = resSet.getPropertyOverRideHash(); + if( overrideHash != null && !overrideHash.isEmpty() ){ + for( Map.Entry<String, Object> entry : overrideHash.entrySet() ){ + String propName = entry.getKey(); + Object propVal = entry.getValue(); + propsStr = propsStr + " [" + propName + " = " + propVal + "]"; + } + } + else { + Iterator<VertexProperty<Object>> pI = resSet.getVert().properties(); + while( pI.hasNext() ){ + VertexProperty<Object> tp = pI.next(); + if( ! tp.key().startsWith("aai") + && ! tp.key().equals("source-of-truth") + //&& ! tp.key().equals("resource-version") + && ! tp.key().startsWith("last-mod") + ) + { + propsStr = propsStr + " [" + tp.key() + " = " + tp.value() + "]"; + } + } + } + // Show the "extra" lookup values too + Map<String,Object> extraPropHash = resSet.getExtraPropertyHash(); + if( extraPropHash != null && !extraPropHash.isEmpty() ){ + for( Map.Entry<String, Object> entry : extraPropHash.entrySet() ){ + String propName = entry.getKey(); + Object propVal = entry.getValue(); + propsStr = propsStr + " [" + propName + " = " + propVal.toString() + "]"; + } + } + + System.out.println( propsStr ); + LOGGER.info(propsStr); + + if( !resSet.getSubResultSet().isEmpty() ){ + ListIterator<ResultSet> listItr = resSet.getSubResultSet().listIterator(); + while( listItr.hasNext() ){ + showResultSet( listItr.next(), levelCount ); + } + } + + }// end of showResultSet() + + private Iterator<Vertex> traverseIncidentEdges(EdgeType treeType, Vertex startV, String connectedNodeType) throws AAIUnknownObjectException, AAIException { + QueryBuilder builder = this.engine.getQueryBuilder(startV).createEdgeTraversal(treeType, startV, loader.introspectorFromName(connectedNodeType)); + return builder; + } + + private Iterator<Vertex> traverseIncidentEdges(EdgeType treeType, Vertex startV, String... connectedNodeType) throws AAIUnknownObjectException, AAIException { + QueryBuilder[] builders = new QueryBuilder[connectedNodeType.length]; + for (int i = 0; i < connectedNodeType.length; i++) { + builders[i] = this.engine.getQueryBuilder(startV).createEdgeTraversal(EdgeType.TREE, startV, loader.introspectorFromName(connectedNodeType[i])); + } + QueryBuilder builder = this.engine.getQueryBuilder(startV).union(builders); + return builder; + + } + +} + diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ResultSet.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ResultSet.java new file mode 100644 index 0000000..46c9b5e --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphgen/ResultSet.java @@ -0,0 +1,168 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphgen; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.openecomp.aai.dbgen.PropertyLimitDesc; +import com.thinkaurelius.titan.core.TitanVertex; + +public class ResultSet { + private TitanVertex vert; + private String newDataDelFlag; + private String doNotOutputFlag; + private String locationInModelSubGraph; + private List<ResultSet> subResultSet; + private PropertyLimitDesc propertyLimitDesc; + private Map<String,Object> propertyOverRideHash; + private Map<String,Object> extraPropertyHash; + + /** + * Instantiates a new result set. + */ + public ResultSet(){ + this.vert = null; + this.newDataDelFlag = ""; + this.doNotOutputFlag = ""; + this.locationInModelSubGraph = ""; + this.subResultSet = new ArrayList<>(); + this.propertyLimitDesc = null; + this.propertyOverRideHash = new HashMap<>(); + this.extraPropertyHash = new HashMap<>(); + } + + + public void setPropertyLimitDesc(PropertyLimitDesc pld) { + this.propertyLimitDesc = pld; + } + + /** + * Gets the vert. + * + * @return the vert + */ + public TitanVertex getVert(){ + return this.vert; + } + + /** + * Gets the sub result set. + * + * @return the sub result set + */ + public List<ResultSet> getSubResultSet(){ + return this.subResultSet; + } + + /** + * Gets the new data del flag. + * + * @return the new data del flag + */ + public String getNewDataDelFlag(){ + return this.newDataDelFlag; + } + + /** + * Gets the do not output flag. + * + * @return the do not output flag + */ + public String getDoNotOutputFlag(){ + return this.doNotOutputFlag; + } + + /** + * Gets the location in model sub graph. + * + * @return the location in model sub graph + */ + public String getLocationInModelSubGraph(){ + return this.locationInModelSubGraph; + } + + /** + * Gets the property limit desc. + * + * @return the property limit desc + */ + public PropertyLimitDesc getPropertyLimitDesc(){ + return this.propertyLimitDesc; + } + + /** + * Gets the property over ride hash. + * + * @return the property over ride hash + */ + public Map<String,Object> getPropertyOverRideHash(){ + return this.propertyOverRideHash; + } + + /** + * Gets the extra property hash. + * + * @return the extra property hash + */ + public Map<String,Object> getExtraPropertyHash(){ + return this.extraPropertyHash; + } + + + public void setVert(TitanVertex vert) { + this.vert = vert; + } + + + public void setNewDataDelFlag(String newDataDelFlag) { + this.newDataDelFlag = newDataDelFlag; + } + + + public void setDoNotOutputFlag(String doNotOutputFlag) { + this.doNotOutputFlag = doNotOutputFlag; + } + + + public void setLocationInModelSubGraph(String locationInModelSubGraph) { + this.locationInModelSubGraph = locationInModelSubGraph; + } + + + public void setSubResultSet(List<ResultSet> subResultSet) { + this.subResultSet = subResultSet; + } + + + public void setPropertyOverRideHash(Map<String, Object> propertyOverRideHash) { + this.propertyOverRideHash = propertyOverRideHash; + } + + + public void setExtraPropertyHash(Map<String, Object> extraPropertyHash) { + this.extraPropertyHash = extraPropertyHash; + } + + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/RelationshipGraph.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/RelationshipGraph.java new file mode 100644 index 0000000..8277b2c --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/RelationshipGraph.java @@ -0,0 +1,299 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphmap; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections.map.MultiValueMap; +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.dynamic.DynamicType; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import org.openecomp.aai.dbgen.DbMeth; +import org.openecomp.aai.dbgraphgen.DbEdgeGroup; +import org.openecomp.aai.domain.model.AAIResources; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import org.openecomp.aai.util.RestURL; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + +public class RelationshipGraph { + + /** + * this method processes the one relationship for the startVertex that is + * sent. + * + * @param g the g + * @param startVertex the start vertex + * @param jaxbContext the jaxb context + * @param rel the rel + * @param aaiExtMap the aai ext map + * @throws AAIException the AAI exception + */ + public static void updRelationship(TitanTransaction g, TitanVertex startVertex, + DynamicJAXBContext jaxbContext, + DynamicEntity rel, + AAIExtensionMap aaiExtMap) + throws AAIException { + String apiVersion = aaiExtMap.getApiVersion(); + String transId = aaiExtMap.getTransId(); + String fromAppId = aaiExtMap.getFromAppId(); + MultiValueMap relatedNodesMap = new MultiValueMap(); + + if( rel != null ){ + HashMap<String, Object> propFilterHash = new HashMap<String, Object>(); + poplatePropertyHashWithRelData(rel, apiVersion, propFilterHash); + String relNodeType = (String)rel.get("relatedTo"); + relatedNodesMap.put(relNodeType, propFilterHash); + } + DbEdgeGroup.replaceEdgeGroup(transId, fromAppId, g, startVertex, + "ONLY_PASSED_COUSINS_REL", relatedNodesMap, apiVersion); + + } + + /** + * Poplate property hash with rel data. + * + * @param rel the rel + * @param apiVersion the api version + * @param propFilterHash the prop filter hash + * @throws AAIException the AAI exception + */ + private static void poplatePropertyHashWithRelData(DynamicEntity rel, String apiVersion, + HashMap<String, Object> propFilterHash) throws AAIException { + + for( DynamicEntity relData: (List<DynamicEntity>)rel.get("relationshipData")) { + String prop = ((String)relData.get("relationshipKey")).toLowerCase().trim(); + propFilterHash.put(prop, ((String)relData.get("relationshipValue")).trim()); + } + } + + /** + * this method gets any relationships for the startVertex being processed + * and sets the related-link. + * + * @param g the g + * @param startVertex the start vertex + * @param apiVersion the api version + * @param aaiExtMap the aai ext map + * @return the relationships + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static DynamicEntity getRelationships(TitanTransaction g, TitanVertex startVertex, + String apiVersion, AAIExtensionMap aaiExtMap) + throws AAIException, UnsupportedEncodingException { + + DynamicType relationshipsType = null; + DynamicType relationshipType = null; + DynamicType relationshipDataType = null; + DynamicType relatedToPropertyType = null; + + Boolean setRelatedToProperty = true; + + AAIResources aaiResources = org.openecomp.aai.ingestModel.IngestModelMoxyOxm.aaiResourceContainer + .get(apiVersion); + + DynamicJAXBContext jaxbContext = aaiResources.getJaxbContext(); + + //String apiVersion = aaiExtMap.getApiVersion(); + String transId = aaiExtMap.getTransId(); + String fromAppId = aaiExtMap.getFromAppId(); + + HashMap <String, String> vidToNodeTypeHash = new HashMap <String, String>(); + HashMap <String, TitanVertex> vidToVertexHash = new HashMap <String, TitanVertex>(); + + if ("v2".equals( apiVersion)) { + relationshipsType = jaxbContext.getDynamicType("inventory.aai.openecomp.org.RelationshipList"); + relationshipType = jaxbContext.getDynamicType(".org.Relationship"); + relationshipDataType = jaxbContext.getDynamicType("inventory.aai.openecomp.org.RelationshipData"); + setRelatedToProperty = false; + } else { + relationshipsType = jaxbContext.getDynamicType("inventory.aai.openecomp.org." + apiVersion + ".RelationshipList"); + relationshipType = jaxbContext.getDynamicType("inventory.aai.openecomp.org." + apiVersion + ".Relationship"); + relationshipDataType = jaxbContext.getDynamicType("inventory.aai.openecomp.org." + apiVersion + ".RelationshipData"); + relatedToPropertyType = jaxbContext.getDynamicType("inventory.aai.openecomp.org." + apiVersion + ".RelatedToProperty"); + if (relatedToPropertyType == null) { + setRelatedToProperty = false; // some versions do not support this + } + } + + DynamicEntity relationships = relationshipsType.newDynamicEntity(); + List<DynamicEntity> listOfRelationships = new ArrayList<DynamicEntity>(); + + DbEdgeGroup.getEdgeGroup(transId, + fromAppId, + g, + startVertex, + vidToNodeTypeHash, + vidToVertexHash, + "ONLY_COUSIN_REL", + AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + // Convert the found relationships to a RelationshipList DynamicEntity + for( Map.Entry<String, TitanVertex> entry : vidToVertexHash.entrySet() ){ + + List<DynamicEntity> relationshipDataList = new ArrayList<DynamicEntity>(); + List<DynamicEntity> relatedToPropertyList = new ArrayList<DynamicEntity>(); + + DynamicEntity relationship = relationshipType.newDynamicEntity(); + + TitanVertex relNode = entry.getValue(); + String relNodeVid = entry.getKey(); + String relNodeType = vidToNodeTypeHash.get(relNodeVid); + String relNodeURL = RestURL.get(g, relNode, apiVersion); + + HashMap <String, Object> nodeKeyPropsHash = RestURL.getKeyHashes(g, relNode, AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + Iterator <Map.Entry<String,Object>>keyIterator = nodeKeyPropsHash.entrySet().iterator(); + while( keyIterator.hasNext() ){ + DynamicEntity relationshipData = relationshipDataType.newDynamicEntity(); + Map.Entry <String,Object>pair = (Map.Entry<String,Object>)keyIterator.next(); + String key = pair.getKey(); + + if (!key.contains(".")) { + key = relNodeType + "." + key; + } + + String value = ""; + if( pair.getValue() != null ){ + value = pair.getValue().toString(); + } + + relationshipData.set("relationshipKey", key); + relationshipData.set("relationshipValue", value); + + relationshipDataList.add(relationshipData); + } + + if (setRelatedToProperty) { + HashMap <String, Object> nodeNamePropsHash = DbMeth.getNodeNamePropHash(transId, fromAppId, g, relNode, AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + Iterator <Map.Entry<String,Object>>nameIterator = nodeNamePropsHash.entrySet().iterator(); + while( nameIterator.hasNext() ){ + DynamicEntity relatedToProperty = relatedToPropertyType.newDynamicEntity(); + Map.Entry <String,Object>pair = (Map.Entry<String,Object>)nameIterator.next(); + String key = pair.getKey(); + + if (!key.contains(".")) { + key = relNodeType + "." + key; + } + + String value = ""; + if( pair.getValue() != null ){ + value = pair.getValue().toString(); + } + relatedToProperty.set("propertyKey", key); + relatedToProperty.set("propertyValue", value); + + relatedToPropertyList.add(relatedToProperty); + + } + relationship.set("relatedToProperty", relatedToPropertyList); + } + relationship.set("relatedTo", relNodeType); + relationship.set("relatedLink", relNodeURL); + relationship.set("relationshipData", relationshipDataList); + + listOfRelationships.add(relationship); + } + relationships.set("relationship", listOfRelationships); + return relationships; + } + + /** + * this method processes any relationships for the startVertex being + * processed. + * + * @param g the g + * @param startVertex the start vertex + * @param jaxbContext the jaxb context + * @param relationshipList the relationship list + * @param aaiExtMap the aai ext map + * @throws AAIException the AAI exception + */ + public static void updRelationships(TitanTransaction g, TitanVertex startVertex, + DynamicJAXBContext jaxbContext, + DynamicEntity relationshipList, + AAIExtensionMap aaiExtMap) + throws AAIException { + + String apiVersion = aaiExtMap.getApiVersion(); + String transId = aaiExtMap.getTransId(); + String fromAppId = aaiExtMap.getFromAppId(); + MultiValueMap relatedNodesMap = new MultiValueMap(); + if (relationshipList != null) { + if( relationshipList.get("relationship") != null ){ + List <DynamicEntity> relListTmp = relationshipList.get("relationship"); + for( DynamicEntity rel: relListTmp) { + HashMap<String, Object> propFilterHash = new HashMap<String, Object>(); + poplatePropertyHashWithRelData(rel, apiVersion, propFilterHash); + String relNodeType = (String)rel.get("relatedTo"); + relatedNodesMap.put(relNodeType, propFilterHash); + + + } + } + DbEdgeGroup.replaceEdgeGroup(transId, fromAppId, g, startVertex, + "ALL_COUSIN_REL", relatedNodesMap, apiVersion); + } + } + + /** + * this method deletes the relationship sent in for the startVertex being + * processed. + * + * @param g the g + * @param startVertex the start vertex + * @param jaxbContext the jaxb context + * @param rel the rel + * @param aaiExtMap the aai ext map + * @throws AAIException the AAI exception + */ + public static void delRelationship(TitanTransaction g, TitanVertex startVertex, + DynamicJAXBContext jaxbContext, + DynamicEntity rel, + AAIExtensionMap aaiExtMap) + throws AAIException { + + String apiVersion = aaiExtMap.getApiVersion(); + String transId = aaiExtMap.getTransId(); + String fromAppId = aaiExtMap.getFromAppId(); + MultiValueMap relatedNodesMap = new MultiValueMap(); + + if( rel != null ){ + HashMap<String, Object> propFilterHash = new HashMap<String, Object>(); + poplatePropertyHashWithRelData(rel, apiVersion, propFilterHash); + String relNodeType = (String)rel.get("relatedTo"); + relatedNodesMap.put(relNodeType, propFilterHash); + } + + DbEdgeGroup.deleteEdgeGroup(transId, fromAppId, g, startVertex, + relatedNodesMap, apiVersion); + + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/SearchGraph.java b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/SearchGraph.java new file mode 100644 index 0000000..f45c3f5 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dbgraphmap/SearchGraph.java @@ -0,0 +1,1207 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dbgraphmap; + +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilderException; +import javax.xml.bind.JAXBException; +import javax.xml.transform.stream.StreamSource; + +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.structure.Vertex; +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.dynamic.DynamicType; +import org.eclipse.persistence.exceptions.DynamicException; +import org.eclipse.persistence.jaxb.JAXBMarshaller; +import org.eclipse.persistence.jaxb.JAXBUnmarshaller; +import org.eclipse.persistence.jaxb.MarshallerProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import org.openecomp.aai.db.DbMethHelper; +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.dbgen.PropertyLimitDesc; +import org.openecomp.aai.dbgraphgen.ModelBasedProcessing; +import org.openecomp.aai.dbgraphgen.ResultSet; +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.domain.model.AAIResources; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.LoaderFactory; +import org.openecomp.aai.introspection.ModelType; +import org.openecomp.aai.introspection.PropertyPredicates; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.parsers.relationship.RelationshipToURI; +import org.openecomp.aai.query.builder.QueryBuilder; +import org.openecomp.aai.schema.enums.ObjectMetadata; +import org.openecomp.aai.schema.enums.PropertyMetadata; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.db.EdgeRule; +import org.openecomp.aai.serialization.db.EdgeRules; +import org.openecomp.aai.serialization.engines.QueryStyle; +import org.openecomp.aai.serialization.engines.TitanDBEngine; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import org.openecomp.aai.util.AAIApiServerURLBase; +import org.openecomp.aai.util.AAIApiVersion; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import org.openecomp.aai.util.PojoUtils; +import org.openecomp.aai.util.RestURL; +import org.openecomp.aai.util.StoreNotificationEvent; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.base.CaseFormat; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + +import edu.emory.mathcs.backport.java.util.Collections; + +/** + * Database Mapping class which acts as the middle man between the REST interface objects + * for the Search namespace + + */ +public class SearchGraph { + + private final String COMPONENT = "aaidbmap"; + private AAIExtensionMap aaiExtMap; + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SearchGraph.class); + /** + * Get the search result based on the includeNodeType and depth provided. + * + * @param fromAppId the from app id + * @param transId the trans id + * @param startNodeType the start node type + * @param startNodeKeyParams the start node key params + * @param includeNodeTypes the include node types + * @param depth the depth + * @param aaiExtMap the aai ext map + * @return Response + * @throws AAIException the AAI exception + */ + public Response runGenericQuery ( + HttpHeaders headers, + String startNodeType, + List <String> startNodeKeyParams, + List <String> includeNodeTypes, + final int depth, + TransactionalGraphEngine dbEngine, + Loader loader, + UrlBuilder urlBuilder) throws AAIException { + Response response = null; + boolean success = true; + String result = ""; + try { + dbEngine.startTransaction(); + + if( startNodeType == null ){ + throw new AAIException("AAI_6120", "null start-node-type passed to the generic query"); + } + + if( startNodeKeyParams == null ){ + throw new AAIException("AAI_6120", "no key param passed to the generic query"); + } + + if( includeNodeTypes == null ){ + throw new AAIException("AAI_6120", "no include params passed to the generic query"); + } + + if (depth > 6) { + throw new AAIException("AAI_6120", "The maximum depth supported by the generic query is 6"); + } + final QueryBuilder queryBuilder; + + // there is an issue with service-instance - it is a unique node but still dependent + // for now query it directly without attempting to craft a valid URI + if (startNodeType.equalsIgnoreCase("service-instance")) { + Introspector obj = loader.introspectorFromName(startNodeType); + // Build a hash with keys to uniquely identify the start Node + String keyName = null; + String keyValue = null; + + QueryBuilder builder = dbEngine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, "service-instance"); + for( String keyData : startNodeKeyParams ){ + int colonIndex = keyData.indexOf(":"); + if( colonIndex <= 0 ){ + throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); + } + else { + keyName = keyData.substring(0, colonIndex).split("\\.")[1]; + keyValue = keyData.substring(colonIndex + 1); + builder.getVerticesByProperty(keyName, keyValue); + + } + } + + queryBuilder = builder; + } else { + URI uri = craftUriFromQueryParams(loader, startNodeType, startNodeKeyParams); + queryBuilder = dbEngine.getQueryBuilder().createQueryFromURI(uri).getQueryBuilder(); + } + List<Vertex> results = queryBuilder.toList(); + if( results.isEmpty()){ + throw new AAIException("AAI_6114", "No Node of type " + + startNodeType + + " found for properties: " + + startNodeKeyParams.toString()); + } else if (results.size() > 1) { + String detail = "More than one Node found by getUniqueNode for params: " + startNodeKeyParams.toString() + "\n"; + throw new AAIException("AAI_6112", detail); + } + + Vertex startNode = results.get(0); + + Collection <Vertex> ver = new HashSet <>(); + List<Vertex> queryResults = new ArrayList<>(); + GraphTraversalSource traversalSource = dbEngine.asAdmin().getReadOnlyTraversalSource(); + GraphTraversal<Vertex, Vertex> traversal; + if (includeNodeTypes.contains(startNodeType) || depth == 0 || includeNodeTypes.contains("all") ) + ver.add(startNode); + + // Now look for a node of includeNodeType within a given depth + traversal = traversalSource.withSideEffect("x", ver).V(startNode) + .times(depth).repeat(__.both().store("x")).cap("x").unfold(); + + if (!includeNodeTypes.contains("all")) { + traversal.where(__.has(AAIProperties.NODE_TYPE, P.within(includeNodeTypes))); + } + queryResults = traversal.toList(); + + + if( queryResults.isEmpty()){ + LOGGER.warn("No nodes found - apipe was null/empty"); + } + else { + + Introspector searchResults = loader.introspectorFromName("search-results"); + List<Object> resultDataList = searchResults.getValue("result-data"); + for (Vertex thisNode: queryResults){ + String nodeType = thisNode.<String>property(AAIProperties.NODE_TYPE).orElse(null); + + String thisNodeURL = urlBuilder.pathed(thisNode); + Introspector resultData = loader.introspectorFromName("result-data"); + + resultData.setValue("resource-type", nodeType); + resultData.setValue("resource-link", thisNodeURL); + resultDataList.add(resultData.getUnderlyingObject()); + + } + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + org.openecomp.aai.introspection.MarshallerProperties properties = new org.openecomp.aai.introspection.MarshallerProperties.Builder( + org.openecomp.aai.restcore.MediaType.getEnum(outputMediaType)).build(); + + result = searchResults.marshal(properties); + response = Response.ok().entity(result).build(); + + LOGGER.debug(ver.size() + " node(s) traversed, " + resultDataList.size() + " found"); + } + success = true; + } catch (AAIException e) { + success = false; + throw e; + } catch (Exception e) { + success = false; + throw new AAIException("AAI_5105", e); + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + dbEngine.rollback(); + } + } + + } + + return response; + } + + private URI craftUriFromQueryParams(Loader loader, String startNodeType, List<String> startNodeKeyParams) throws UnsupportedEncodingException, IllegalArgumentException, UriBuilderException, AAIException { + Introspector relationship = loader.introspectorFromName("relationship"); + + relationship.setValue("related-to", startNodeType); + List<Object> relationshipDataList = relationship.getValue("relationship-data"); + + for( String keyData : startNodeKeyParams ){ + int colonIndex = keyData.indexOf(":"); + if( colonIndex <= 0 ){ + throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); + } + else { + Introspector data = loader.introspectorFromName("relationship-data"); + data.setValue("relationship-key", keyData.substring(0, colonIndex)); + data.setValue("relationship-value", keyData.substring(colonIndex + 1)); + relationshipDataList.add(data.getUnderlyingObject()); + } + } + + RelationshipToURI parser = new RelationshipToURI(loader, relationship); + + return parser.getUri(); + } + + /** + * Run nodes query. + * + * @param fromAppId the from app id + * @param transId the trans id + * @param targetNodeType the target node type + * @param edgeFilterParams the edge filter params + * @param filterParams the filter params + * @param aaiExtMap the aai ext map + * @return Response + * @throws AAIException the AAI exception + */ + public Response runNodesQuery ( + HttpHeaders headers, + String targetNodeType, + List <String> edgeFilterParams, + List <String> filterParams, + TransactionalGraphEngine dbEngine, + Loader loader, + UrlBuilder urlBuilder) throws AAIException { + + Response response = null; + boolean success = true; + String result = ""; + final String EQUALS = "EQUALS"; + final String DOES_NOT_EQUAL = "DOES-NOT-EQUAL"; + final String EXISTS = "EXISTS"; + final String DOES_NOT_EXIST = "DOES-NOT-EXIST"; + try { + + dbEngine.startTransaction(); + + Introspector target; + + if( targetNodeType == null || targetNodeType == "" ){ + throw new AAIException("AAI_6120", "null or empty target-node-type passed to the node query"); + } + + try { + target = loader.introspectorFromName(targetNodeType); + } catch (AAIUnknownObjectException e) { + throw new AAIException("AAI_6115", "Unrecognized nodeType [" + targetNodeType + "] passed to node query."); + } + + if( filterParams.isEmpty() && edgeFilterParams.isEmpty()){ + // For now, it's ok to pass no filter params. We'll just return ALL the nodes of the requested type. + LOGGER.warn("No filters passed to the node query"); + } + + StringBuilder queryStringForMsg = new StringBuilder(); + GraphTraversal<Vertex, Vertex> traversal = dbEngine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE, targetNodeType); + queryStringForMsg.append("has(\"aai-node-type\"," + targetNodeType + ")"); + + for( String filter : filterParams ) { + String [] pieces = filter.split(":"); + if( pieces.length < 2 ){ + throw new AAIException("AAI_6120", "bad filter passed to node query: [" + filter + "]"); + } + else { + String propName = this.findDbPropName(target, pieces[0]); + String filterType = pieces[1]; + if( filterType.equals(EQUALS)){ + if( pieces.length < 3 ){ + throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); + } + String value = "?"; + if( pieces.length == 3 ){ + value = pieces[2]; + } + else if( pieces.length > 3 ){ + // When a ipv6 address comes in as a value, it has colons in it which require us to + // pull the "value" off the end of the filter differently + int startPos4Value = propName.length() + filterType.length() + 3; + value = filter.substring(startPos4Value); + } + queryStringForMsg.append(".has(" + propName + "," + value + ")"); + traversal.has(propName,value); + } + else if( filterType.equals(DOES_NOT_EQUAL)){ + if( pieces.length < 3 ){ + throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); + } + String value = "?"; + if( pieces.length == 3 ){ + value = pieces[2]; + } + else if( pieces.length > 3 ){ + // When a ipv6 address comes in as a value, it has colons in it which require us to + // pull the "value" off the end of the filter differently + int startPos4Value = propName.length() + filterType.length() + 3; + value = filter.substring(startPos4Value); + } + queryStringForMsg.append(".hasNot(" + propName + "," + value + ")"); + traversal.not(__.has(propName,value)); + } + else if( filterType.equals(EXISTS)){ + queryStringForMsg.append(".has(" + propName + ")"); + traversal.has(propName); + } + else if( filterType.equals(DOES_NOT_EXIST)){ + queryStringForMsg.append(".hasNot(" + propName + ")"); + traversal.hasNot(propName); + } + else { + throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); + } + } + } + + if (!edgeFilterParams.isEmpty()) { + // edge-filter=pserver:EXISTS: OR pserver:EXISTS:hostname:XXX + // edge-filter=pserver:DOES-NOT-EXIST: OR pserver:DOES-NOT-EXIST:hostname:XXX + String filter = edgeFilterParams.get(0); // we process and allow only one edge filter for now + String [] pieces = filter.split(":"); + if( pieces.length < 2 || pieces.length == 3 || pieces.length > 4){ + throw new AAIException("AAI_6120", "bad edge-filter passed: [" + filter + "]"); + } else { + String nodeType = pieces[0].toLowerCase(); + String filterType = pieces[1].toUpperCase(); + Introspector otherNode; + if (!filterType.equals(EXISTS) && !filterType.equals(DOES_NOT_EXIST)) { + throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); + } + try { + otherNode = loader.introspectorFromName(nodeType); + } catch (AAIUnknownObjectException e) { + throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to node query."); + } + String propName = null; + String propValue = null; + if ( pieces.length >= 3) { + propName = this.findDbPropName(otherNode, pieces[2].toLowerCase()); + propValue = pieces[3]; + } + String[] edgeLabels = getEdgeLabel(targetNodeType, nodeType); + + GraphTraversal<Vertex, Vertex> edgeSearch = __.start(); + + edgeSearch.both(edgeLabels); + if (propName != null) { + // check for matching property + if (propValue != null) { + edgeSearch.has(propName, propValue); + } else { + edgeSearch.has(propName); + } + } + + if( filterType.equals(DOES_NOT_EXIST)){ + traversal.where(__.not(edgeSearch)); + } else if (filterType.equals(EXISTS)) { + traversal.where(edgeSearch); + } + } + } + + List<Vertex> results = traversal.toList(); + Introspector searchResults = loader.introspectorFromName("search-results"); + List<Object> resultDataList = searchResults.getValue("result-data"); + for (Vertex thisNode: results){ + String nodeType = thisNode.<String>property(AAIProperties.NODE_TYPE).orElse(null); + + String thisNodeURL = urlBuilder.pathed(thisNode); + Introspector resultData = loader.introspectorFromName("result-data"); + + resultData.setValue("resource-type", nodeType); + resultData.setValue("resource-link", thisNodeURL); + resultDataList.add(resultData.getUnderlyingObject()); + + } + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + org.openecomp.aai.introspection.MarshallerProperties properties = new org.openecomp.aai.introspection.MarshallerProperties.Builder( + org.openecomp.aai.restcore.MediaType.getEnum(outputMediaType)).build(); + + result = searchResults.marshal(properties); + response = Response.ok().entity(result).build(); + + success = true; + } catch (AAIException e) { + success = false; + throw e; + } catch (Exception e) { + success = false; + throw new AAIException("AAI_5105", e); + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + dbEngine.rollback(); + } + } + } + + return response; + } + + private String findDbPropName(Introspector obj, String propName) { + + Optional<String> result = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS); + if (result.isPresent()) { + return result.get(); + } else { + return propName; + } + } + + + /** + * Gets the edge label. + * + * @param targetNodeType the target node type + * @param nodeType the node type + * @return the edge label + * @throws AAIException the AAI exception + */ + public static String[] getEdgeLabel(String targetNodeType, String nodeType) throws AAIException{ + Map<String, EdgeRule> rules = EdgeRules.getInstance().getEdgeRules(targetNodeType, nodeType); + String[] results = rules.keySet().toArray(new String[0]); + return results; + } + + + /** + * Run named query. + * + * @param fromAppId the from app id + * @param transId the trans id + * @param queryParameters the query parameters + * @param aaiExtMap the aai ext map + * @return the response + * @throws JAXBException the JAXB exception + * @throws AAIException the AAI exception + */ + public Response runNamedQuery(String fromAppId, String transId, String queryParameters, + DBConnectionType connectionType, + AAIExtensionMap aaiExtMap) throws JAXBException, AAIException { + // TODO Auto-generated method stub + AAIResources aaiResources = org.openecomp.aai.ingestModel.IngestModelMoxyOxm.aaiResourceContainer + .get(aaiExtMap.getApiVersion()); + DynamicJAXBContext jaxbContext = aaiResources.getJaxbContext(); + JAXBUnmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + + //String dynamicClass = aaiRes.getResourceClassName(); + + Introspector inventoryItems; + boolean success = true; + TitanTransaction g = null; + TransactionalGraphEngine dbEngine = null; + try { + + Loader loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST); + dbEngine = new TitanDBEngine( + QueryStyle.TRAVERSAL, + connectionType, + loader); + DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId); + ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer); + + g = dbEngine.startTransaction(); + if (aaiExtMap.getHttpServletRequest().getContentType() == null || // default to json + aaiExtMap.getHttpServletRequest().getContentType().contains("application/json")) { + unmarshaller.setProperty("eclipselink.media-type", "application/json"); + unmarshaller.setProperty("eclipselink.json.include-root", false); + } + + if (queryParameters.length() == 0) { + queryParameters = "{}"; + unmarshaller.setProperty("eclipselink.media-type", "application/json"); + unmarshaller.setProperty("eclipselink.json.include-root", false); + } + String dynamicClass = "inventory.aai.ecomp.org." + aaiExtMap.getApiVersion() + ".ModelAndNamedQuerySearch"; + Class<? extends DynamicEntity> resultClass = jaxbContext.newDynamicEntity(dynamicClass).getClass(); + + StringReader reader = new StringReader(queryParameters); + + DynamicEntity modelAndNamedQuerySearch = (DynamicEntity) unmarshaller.unmarshal(new StreamSource(reader), resultClass).getValue(); + + if (modelAndNamedQuerySearch == null) { + throw new AAIException("AAI_5105"); + } + + HashMap<String,Object> namedQueryLookupHash = new HashMap<String,Object>(); + + DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters"); + + String namedQueryUuid = null; + if (qp.isSet("namedQuery")) { + DynamicEntity namedQuery = (DynamicEntity) qp.get("namedQuery"); + + if (namedQuery.isSet("namedQueryUuid")) { + namedQueryUuid = namedQuery.get("namedQueryUuid"); + } + if (namedQuery.isSet("namedQueryName")) { + namedQueryLookupHash.put("named-query-name", namedQuery.get("namedQueryName")); + } + if (namedQuery.isSet("namedQueryVersion")) { + namedQueryLookupHash.put("named-query-version", namedQuery.get("namedQueryVersion")); + } + } + + if (namedQueryUuid == null) { + + DbMethHelper dbMethHelper = new DbMethHelper(loader, dbEngine); + List<Vertex> namedQueryVertices = dbMethHelper.locateUniqueVertices("named-query", namedQueryLookupHash); + for (Vertex vert : namedQueryVertices) { + namedQueryUuid = vert.<String>property("named-query-uuid").orElse(null); + // there should only be one, we'll pick the first if not + break; + } + } + + String secondaryFilterCutPoint = null; + + if (modelAndNamedQuerySearch.isSet("secondaryFilterCutPoint")) { + secondaryFilterCutPoint = modelAndNamedQuerySearch.get("secondaryFilterCutPoint"); + } + + List<Map<String,Object>> startNodeFilterHash = new ArrayList<>(); + + mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), + startNodeFilterHash, jaxbContext); + + Map<String,Object> secondaryFilterHash = new HashMap<>(); + + mapSecondaryFilters((DynamicEntity)modelAndNamedQuerySearch.get("secondaryFilts"), + secondaryFilterHash, jaxbContext); + + List<ResultSet> resultSet = processor.queryByNamedQuery(transId, fromAppId, + namedQueryUuid, startNodeFilterHash, aaiExtMap.getApiVersion(), secondaryFilterCutPoint, secondaryFilterHash); + + inventoryItems = loader.introspectorFromName("inventory-response-items"); + + List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer); + + inventoryItems.setValue("inventory-response-item", invItemList); + success = true; + } catch (AAIException e) { + success = false; + throw e; + } catch (Exception e) { + success = false; + throw new AAIException("AAI_5105", e); + } finally { + if (g != null) { + if (success) { + g.commit(); + } else { + g.rollback(); + } + } + } + + return getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders()); + } + + + /** + * Execute model operation. + * + * @param fromAppId the from app id + * @param transId the trans id + * @param queryParameters the query parameters + * @param isDelete the is delete + * @param aaiExtMap the aai ext map + * @return the response + * @throws JAXBException the JAXB exception + * @throws AAIException the AAI exception + * @throws DynamicException the dynamic exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public Response executeModelOperation(String fromAppId, String transId, String queryParameters, + DBConnectionType connectionType, + boolean isDelete, + AAIExtensionMap aaiExtMap) throws JAXBException, AAIException, DynamicException, UnsupportedEncodingException { + // TODO Auto-generated method stub + AAIResources aaiResources = org.openecomp.aai.ingestModel.IngestModelMoxyOxm.aaiResourceContainer + .get(aaiExtMap.getApiVersion()); + DynamicJAXBContext jaxbContext = aaiResources.getJaxbContext(); + JAXBUnmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + + //String dynamicClass = aaiRes.getResourceClassName(); + Response response; + boolean success = true; + TitanTransaction g = null; + TransactionalGraphEngine dbEngine = null; + try { + + Loader loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST); + dbEngine = new TitanDBEngine( + QueryStyle.TRAVERSAL, + connectionType, + loader); + DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId); + ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer); + g = dbEngine.startTransaction(); + + + if (aaiExtMap.getHttpServletRequest().getContentType() == null || // default to json + aaiExtMap.getHttpServletRequest().getContentType().contains("application/json")) { + unmarshaller.setProperty("eclipselink.media-type", "application/json"); + unmarshaller.setProperty("eclipselink.json.include-root", false); + } + + if (queryParameters.length() == 0) { + queryParameters = "{}"; + unmarshaller.setProperty("eclipselink.media-type", "application/json"); + unmarshaller.setProperty("eclipselink.json.include-root", false); + } + String dynamicClass = "inventory.aai.openecomp.org." + aaiExtMap.getApiVersion() + ".ModelAndNamedQuerySearch"; + Class<? extends DynamicEntity> resultClass = jaxbContext.newDynamicEntity(dynamicClass).getClass(); + + StringReader reader = new StringReader(queryParameters); + + DynamicEntity modelAndNamedQuerySearch = (DynamicEntity) unmarshaller.unmarshal(new StreamSource(reader), resultClass).getValue(); + + if (modelAndNamedQuerySearch == null) { + throw new AAIException("AAI_5105"); + } + + Map<String,Object> modelQueryLookupHash = new HashMap<>(); + + String modelVersionId = null; + String modelName = null; + String modelInvariantId = null; + String modelVersion = null; + String topNodeType = null; + + if (modelAndNamedQuerySearch.isSet("topNodeType")) { + topNodeType = modelAndNamedQuerySearch.get("topNodeType"); + } + + // the ways to get a model: + + // 1. model-version-id (previously model-name-version-id + // 2. model-invariant-id (previously model-id) + model-version + // 3. model-name + model-version + + // we will support both using the OverloadedModel object in the v9 oxm. This allows us to unmarshal + // either an old-style model or new-style model + model-ver object + if (modelAndNamedQuerySearch.isSet("queryParameters")) { + DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters"); + + if (qp.isSet("model")) { + DynamicEntity model = (DynamicEntity) qp.get("model"); + + // on an old-style model object, the following 4 attrs were all present + if (model.isSet("modelNameVersionId")) { + modelVersionId = model.get("modelNameVersionId"); + } + if (model.isSet("modelId")) { + modelInvariantId = model.get("modelId"); + } + if (model.isSet("modelName")) { + modelName = model.get("modelName"); + } + if (model.isSet("modelVersion")) { + modelVersion = model.get("modelVersion"); + } + + // new style splits model-invariant-id from the other 3 attrs. This is + // the only way to directly look up the model object + if (model.isSet("modelInvariantId")) { + modelInvariantId = model.get("modelInvariantId"); + } + + if (model.isSet("modelVers")) { + // we know that this is new style, because modelVers was not an option + // before v9 + DynamicEntity modelVers = (DynamicEntity) model.get("modelVers"); + if (modelVers.isSet("modelVer")) { + List<DynamicEntity> modelVerList = modelVers.get("modelVer"); + // if they send more than one, too bad, they get the first one + DynamicEntity modelVer = modelVerList.get(0); + if (modelVer.isSet("modelName")) { + modelName = modelVer.get("modelName"); + } + if (modelVer.isSet("modelVersionId")) { + modelVersionId = modelVer.get("modelVersionId"); + } + if (modelVer.isSet("modelVersion")) { + modelVersion = modelVer.get("modelVersion"); + } + } + } + } + } + + List<Map<String,Object>> startNodeFilterHash = new ArrayList<>(); + + String resourceVersion = mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), + startNodeFilterHash, jaxbContext); + + if (isDelete) { + + List<ResultSet> resultSet = processor.queryByModel(transId, fromAppId, + modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() ); + + Map<Object,String> objectToVertMap = new HashMap<>(); + List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer); + + ResultSet rs = resultSet.get(0); + + TitanVertex firstVert = rs.getVert(); + String restURL = RestURL.get(g, firstVert); + + Map<String,String> delResult = processor.runDeleteByModel( transId, fromAppId, + modelVersionId, topNodeType, startNodeFilterHash.get(0), aaiExtMap.getApiVersion(), resourceVersion ); + + String resultStr = ""; + for (Map.Entry<String,String> ent : delResult.entrySet()) { + resultStr += "v[" + ent.getKey() + "] " + ent.getValue() + ",\n"; + } + resultStr.trim(); + + DynamicEntity inventoryItems = jaxbContext.newDynamicEntity("inventory.aai.openecomp.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItems"); + DynamicEntity topInvItem = remapInventoryItems((DynamicEntity)invItemList.get(0), jaxbContext, delResult, objectToVertMap, aaiExtMap); + + List<DynamicEntity> newInvItemList = new ArrayList<DynamicEntity>(); + newInvItemList.add(topInvItem); + inventoryItems.set("inventoryResponseItem", newInvItemList); + + // put the inventoryItems in a UEB notification object + String notificationVersion = AAIConfig.get("aai.notification.current.version"); + + AAIResources aaiNotificationResources = org.openecomp.aai.ingestModel.IngestModelMoxyOxm.aaiResourceContainer + .get(notificationVersion); + + DynamicJAXBContext notificationJaxbContext = aaiNotificationResources.getJaxbContext(); + + DynamicEntity notificationHeader = notificationJaxbContext + .getDynamicType("inventory.aai.openecomp.org." + notificationVersion + ".NotificationEventHeader") + .newDynamicEntity(); + + notificationHeader.set("entityLink", restURL); + notificationHeader.set("action", "DELETE"); + + notificationHeader.set("entityType", "inventory-response-items"); + notificationHeader.set("topEntityType", "inventory-response-items"); + notificationHeader.set("sourceName", aaiExtMap.getFromAppId()); + notificationHeader.set("version", notificationVersion); + + StoreNotificationEvent sne = new StoreNotificationEvent(); + + sne.storeDynamicEvent(notificationJaxbContext, notificationVersion, notificationHeader, inventoryItems); + + response = Response.ok(resultStr).build(); + + } else { + List<ResultSet> resultSet = processor.queryByModel( transId, fromAppId, + modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() ); + + Introspector inventoryItems = loader.introspectorFromName("inventory-response-items"); + + List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer); + + inventoryItems.setValue("inventory-response-item", invItemList); + + response = getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders()); + } + success = true; + } catch (AAIException e) { + success = false; + throw e; + } catch (Exception e) { + success = false; + throw new AAIException("AAI_5105", e); + } finally { + if (g != null) { + if (success) { + g.commit(); + } else { + g.rollback(); + } + } + } + + return response; + } + + private Response getResponseFromIntrospector(Introspector obj, HttpHeaders headers) { + boolean isJson = false; + for (MediaType mt : headers.getAcceptableMediaTypes()) { + if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) { + isJson = true; + break; + } + } + org.openecomp.aai.introspection.MarshallerProperties properties; + if (isJson) { + properties = + new org.openecomp.aai.introspection.MarshallerProperties.Builder(org.openecomp.aai.restcore.MediaType.APPLICATION_JSON_TYPE).build(); + } else { + properties = + new org.openecomp.aai.introspection.MarshallerProperties.Builder(org.openecomp.aai.restcore.MediaType.APPLICATION_XML_TYPE).build(); + } + + String marshalledObj = obj.marshal(properties); + return Response.ok().entity(marshalledObj).build(); + } + + /** + * Map instance filters. + * + * @param instanceFilters the instance filters + * @param startNodeFilterHash the start node filter hash + * @param jaxbContext the jaxb context + * @return the string + */ + private String mapInstanceFilters(DynamicEntity instanceFilters, List<Map<String,Object>> startNodeFilterHash, DynamicJAXBContext jaxbContext) { + + if (instanceFilters == null || !instanceFilters.isSet("instanceFilter")) { + return null; + } + @SuppressWarnings("unchecked") + List<DynamicEntity> instanceFilter = (ArrayList<DynamicEntity>)instanceFilters.get("instanceFilter"); + String resourceVersion = null; + + for (DynamicEntity instFilt : instanceFilter) { + List<DynamicEntity> any = instFilt.get("any"); + HashMap<String,Object> thisNodeFilterHash = new HashMap<String,Object>(); + for (DynamicEntity anyEnt : any) { + String clazz = anyEnt.getClass().getCanonicalName(); + String simpleClazz = anyEnt.getClass().getSimpleName(); + + String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz); + + DynamicType anyEntType = jaxbContext.getDynamicType(clazz); + + for (String propName : anyEntType.getPropertiesNames()) { + // hyphencase the prop and throw it on the hash + if (anyEnt.isSet(propName)) { + thisNodeFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName)); + if (propName.equals("resourceVersion") && resourceVersion == null) { + resourceVersion = (String)anyEnt.get(propName); + } + } + } + } + startNodeFilterHash.add(thisNodeFilterHash); + } + return resourceVersion; + } + + /** + * Map secondary filters. + * + * @param secondaryFilts the secondary filters + * @param secondaryFilterHash the secondary filter hash + * @param jaxbContext the jaxb context + * @return the string + */ + private void mapSecondaryFilters(DynamicEntity secondaryFilts, Map<String,Object> secondaryFilterHash, DynamicJAXBContext jaxbContext) { + + if (secondaryFilts == null || !secondaryFilts.isSet("secondaryFilt")) { + return; + } + @SuppressWarnings("unchecked") + List<DynamicEntity> secondaryFilter = (ArrayList<DynamicEntity>)secondaryFilts.get("secondaryFilt"); + + for (DynamicEntity secondaryFilt : secondaryFilter) { + List<DynamicEntity> any = secondaryFilt.get("any"); + + for (DynamicEntity anyEnt : any) { + String clazz = anyEnt.getClass().getCanonicalName(); + String simpleClazz = anyEnt.getClass().getSimpleName(); + + String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz); + + DynamicType anyEntType = jaxbContext.getDynamicType(clazz); + + for (String propName : anyEntType.getPropertiesNames()) { + // hyphencase the prop and throw it on the hash + if (anyEnt.isSet(propName)) { + secondaryFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName)); + } + } + } + } + } + + /** + * Remap inventory items. + * + * @param invResultItem the inv result item + * @param jaxbContext the jaxb context + * @param includeTheseVertices the include these vertices + * @param objectToVertMap the object to vert map + * @param aaiExtMap the aai ext map + * @return the dynamic entity + */ + private DynamicEntity remapInventoryItems(DynamicEntity invResultItem, DynamicJAXBContext jaxbContext, + Map<String,String> includeTheseVertices, Map<Object,String> objectToVertMap, AAIExtensionMap aaiExtMap) { + + + DynamicEntity inventoryItem = jaxbContext.newDynamicEntity("inventory.aai.ecomp.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItem"); + Object item = invResultItem.get("item"); + inventoryItem.set("modelName", invResultItem.get("modelName")); + inventoryItem.set("item", item); + inventoryItem.set("extraProperties", invResultItem.get("extraProperties")); + + String vertexId = ""; + + if (objectToVertMap.containsKey(item)) { + vertexId = objectToVertMap.get(item); + } + + if (includeTheseVertices.containsKey(vertexId)) { + if (invResultItem.isSet("inventoryResponseItems")) { + List<DynamicEntity> invItemList = new ArrayList<DynamicEntity>(); + DynamicEntity inventoryItems = jaxbContext.newDynamicEntity("inventory.aai.ecomp.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItems"); + DynamicEntity subInventoryResponseItems = invResultItem.get("inventoryResponseItems"); + List<DynamicEntity> subInventoryResponseItemList = subInventoryResponseItems.get("inventoryResponseItem"); + for (DynamicEntity ent : subInventoryResponseItemList) { + DynamicEntity invItem = remapInventoryItems(ent, jaxbContext, includeTheseVertices, objectToVertMap, aaiExtMap); + if (invItem != null) { + invItemList.add(invItem); + } + } + if (invItemList != null) { + inventoryItems.set("inventoryResponseItem", invItemList); + inventoryItem.set("inventoryResponseItems", inventoryItems); + } + } + } + return inventoryItem; + } + + /** + * Unpack result set. + * + * @param g the g + * @param resultSetList the result set list + * @param jaxbContext the jaxb context + * @param aaiResources the aai resources + * @param objectToVertMap the object to vert map + * @param aaiExtMap the aai ext map + * @return the array list + * @throws AAIException the AAI exception + */ + // this should return an inventoryItem + private List<Object> unpackResultSet(List<ResultSet> resultSetList, + TransactionalGraphEngine engine, + Loader loader, + DBSerializer serializer) throws AAIException { + + List<Object> resultList = new ArrayList<>(); + + for (ResultSet resultSet : resultSetList) { + + if( resultSet.getVert() == null ){ + continue; + } + + Introspector inventoryItem = loader.introspectorFromName("inventory-response-item"); + Introspector inventoryItems = loader.introspectorFromName("inventory-response-items"); + // add this inventoryItem to the resultList for this level + resultList.add(inventoryItem.getUnderlyingObject()); + + TitanVertex vert = resultSet.getVert(); + + Long vertId = (Long)vert.longId(); + + String aaiNodeType = vert.<String>property("aai-node-type").orElse(null); + + + + PojoUtils pu = new PojoUtils(); + if (aaiNodeType != null) { + Introspector thisObj = loader.introspectorFromName(aaiNodeType); + + if (resultSet.getExtraPropertyHash() != null) { + Map<String,Object> extraProperties = resultSet.getExtraPropertyHash(); + + Introspector extraPropertiesEntity = loader.introspectorFromName("extra-properties"); + + List<Object> extraPropsList = extraPropertiesEntity.getValue("extra-property"); + + for (Map.Entry<String,Object> ent : extraProperties.entrySet()) { + String propName = ent.getKey(); + Object propVal = ent.getValue(); + + Introspector extraPropEntity = loader.introspectorFromName("extra-property"); + + extraPropEntity.setValue("property-name", propName); + extraPropEntity.setValue("property-value", propVal); + + extraPropsList.add(extraPropEntity.getUnderlyingObject()); + + } + inventoryItem.setValue("extra-properties", extraPropertiesEntity.getUnderlyingObject()); + } + + try { + serializer.dbToObject(Collections.singletonList(vert), thisObj, 0, true, "false"); + } catch (UnsupportedEncodingException e1) { + throw new AAIException("AAI_5105"); + } + PropertyLimitDesc propertyLimitDesc = resultSet.getPropertyLimitDesc(); + + if (propertyLimitDesc != null) { + + if (PropertyLimitDesc.SHOW_NONE.equals(propertyLimitDesc)) { + HashMap<String,Object> emptyPropertyOverRideHash = new HashMap<String,Object>(); + for (String key : thisObj.getAllKeys()) { + emptyPropertyOverRideHash.put(key, null); + } + filterProperties(thisObj, emptyPropertyOverRideHash); + } else if (PropertyLimitDesc.SHOW_ALL.equals(propertyLimitDesc)) { + //keep everything + } else if (PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY.equals(propertyLimitDesc)) { + HashMap<String,Object> keysAndNamesPropHash = new HashMap<String,Object>(); + + for (String key : thisObj.getAllKeys()) { + keysAndNamesPropHash.put(key, null); + } + String namePropMetaData = thisObj.getMetadata(ObjectMetadata.NAME_PROPS); + if (namePropMetaData != null) { + String[] nameProps = namePropMetaData.split(","); + for (String names : nameProps) { + keysAndNamesPropHash.put(names, null); + } + } + filterProperties(thisObj, keysAndNamesPropHash); + } + } else { + if (resultSet.getPropertyOverRideHash() != null && resultSet.getPropertyOverRideHash().size() > 0) { + Map<String,Object> propertyOverRideHash = resultSet.getPropertyOverRideHash(); + if (propertyOverRideHash.containsKey("persona-model-id")) { + propertyOverRideHash.remove("persona-model-id"); + propertyOverRideHash.put("model-invariant-id", null); + } + for (String key : thisObj.getAllKeys()) { + propertyOverRideHash.put(key, null); + } + filterProperties(thisObj, propertyOverRideHash); + } else { + //keep everything + } + } + + if (thisObj != null) { + inventoryItem.setValue("item", thisObj.getUnderlyingObject()); + + String modelName = null; + try { + String modelInvariantIdLocal = (String)vert.<String>property("model-invariant-id-local").orElse(null); // this one points at a model + String modelVersionIdLocal = (String)vert.<String>property("model-version-id-local").orElse(null); // this one points at a model-ver + + if ( (modelInvariantIdLocal != null && modelVersionIdLocal != null) + && (modelInvariantIdLocal.length() > 0 && modelVersionIdLocal.length() > 0) ) { + HashMap<String,Object> modelLookupHash = new HashMap<String,Object>(); + + Introspector modelVer = loader.introspectorFromName("model-ver"); + modelVer.setValue("model-version-id", modelVersionIdLocal); + QueryBuilder builder = engine.getQueryBuilder().createDBQuery(modelVer); + + List<Vertex> modelVerVerts = builder.toList(); + if (modelVerVerts.size() != 1) { + throw new AAIException("AAI_6112"); + } + Vertex modelVerVert = modelVerVerts.get(0); + + modelName = modelVerVert.<String>property("model-name").orElse(null); + + if (modelName != null && modelName.length() > 0) { + inventoryItem.setValue("model-name", modelName); + } + } + } catch (DynamicException e) { + ; // it's ok, dynamic object might not have these fields + } catch (AAIException e) { + if (e.getErrorObject().getErrorCode().equals("6114")) { + // it's ok, couldn't find a matching model + } else { + throw e; + } + } + + if (resultSet.getSubResultSet() != null) { + List<ResultSet> subResultSet = resultSet.getSubResultSet(); + if (subResultSet != null && subResultSet.size() > 0 ) { + List<Object> res = unpackResultSet(subResultSet, engine, loader, serializer); + if (res.size() > 0) { + inventoryItems.setValue("inventory-response-item", res); + inventoryItem.setValue("inventory-response-items", inventoryItems.getUnderlyingObject()); + } + } + } + } + } + } + + return resultList; + } + + private void filterProperties(Introspector thisObj, Map<String, Object> override) { + + thisObj.getProperties().stream().filter(x -> { + return !override.containsKey(x); + }).forEach(prop -> { + if (thisObj.isSimpleType(prop)) { + thisObj.setValue(prop, null); + } + }); + + } + + /** + * Gets the media type. + * + * @param mediaTypeList the media type list + * @return the media type + */ + protected String getMediaType(List <MediaType> mediaTypeList) { + String mediaType = MediaType.APPLICATION_JSON; // json is the default + for (MediaType mt : mediaTypeList) { + if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { + mediaType = MediaType.APPLICATION_XML; + } + } + return mediaType; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSConsumer.java b/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSConsumer.java new file mode 100644 index 0000000..d8a0ce7 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSConsumer.java @@ -0,0 +1,163 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dmaap; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; +import javax.ws.rs.core.MediaType; + +import org.apache.log4j.MDC; +import org.eclipse.jetty.util.security.Password; +import org.json.JSONException; +import org.json.JSONObject; + +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.util.AAIConstants; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.nsa.mr.client.MRBatchingPublisher; +import com.att.nsa.mr.client.MRClientFactory; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; + +public class AAIDmaapEventJMSConsumer implements MessageListener { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIDmaapEventJMSConsumer.class); + + private final static String COMPONENT = "aaiDmaapEvent"; + private MRBatchingPublisher adp = null; + + private Properties props; + + private String username; + private String password; + private String contentType; + + private String url; + private Client client; + + public AAIDmaapEventJMSConsumer() throws org.apache.commons.configuration.ConfigurationException { + super(); + + if (this.adp == null) { + try { + FileReader reader = new FileReader(new File(AAIConstants.AAI_EVENT_DMAAP_PROPS)); + props = new Properties(); + props.load(reader); + props.setProperty("DME2preferredRouterFilePath", AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "preferredRoute.txt"); + if (props.getProperty("password") != null && props.getProperty("password").startsWith("OBF:")) { + props.setProperty("password", Password.deobfuscate(props.getProperty("password"))); + } + this.adp = MRClientFactory.createBatchingPublisher(props); + + String host = props.getProperty("host"); + String topic = props.getProperty("topic"); + String protocol = props.getProperty("Protocol"); + + username = props.getProperty("username"); + password = props.getProperty("password"); + contentType = props.getProperty("contenttype"); + + url = protocol + "://" + host + "/events/" + topic; + client = Client.create(); + client.addFilter(new HTTPBasicAuthFilter(username, password)); + + } catch (IOException e) { + ErrorLogHelper.logError("AAI_4000", "Error updating dmaap config file for aai event."); + } + } + + } + + @Override + public void onMessage(Message message) { + + String jsmMessageTxt = ""; + String aaiEvent = ""; + String transId = ""; + String fromAppId = ""; + String fullId = ""; + + if (message instanceof TextMessage) { + try { + jsmMessageTxt = ((TextMessage) message).getText(); + JSONObject jo = new JSONObject(jsmMessageTxt); + + if (jo.has("aaiEventPayload")) { + aaiEvent = jo.getJSONObject("aaiEventPayload").toString(); + } else { + return; + } + if (jo.getString("transId") != null) { + MDC.put("requestId", jo.getString("transId")); + } + if (jo.getString("fromAppId") != null) { + MDC.put("partnerName", jo.getString("fromAppId")); + } + if (jo.getString("fullId") != null) { + fullId = jo.getString("fullId"); + } + + LOGGER.info(fullId + "|" + transId + "|" + fromAppId + "|" + aaiEvent); + + String environment = System.getProperty("lrmRO"); + + if (environment == null) { + this.adp.send(aaiEvent); + } else { + if (environment.startsWith("dev") || environment.startsWith("testINT") || environment.startsWith("testEXT")) { + + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept(contentType).type(MediaType.APPLICATION_JSON).post(ClientResponse.class, aaiEvent); + + if (response.getStatus() != 200) { + System.out.println("Failed : HTTP error code : " + response.getStatus()); + } + } else { + this.adp.send(aaiEvent); + } + } + + } catch (IOException e) { + if (e instanceof java.net.SocketException) { + if (e.getMessage().contains("Connection reset")) { + } else { + ErrorLogHelper.logError("AAI_7304", "Error reaching DMaaP to send event. " + aaiEvent); + } + } else { + ErrorLogHelper.logError("AAI_7304", "Error reaching DMaaP to send event. " + aaiEvent); + } + } catch (JMSException | JSONException e) { + ErrorLogHelper.logError("AAI_7350", "Error parsing aaievent jsm message for sending to dmaap. " + jsmMessageTxt); + } + } + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSProducer.java b/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSProducer.java new file mode 100644 index 0000000..305e585 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/dmaap/AAIDmaapEventJMSProducer.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.dmaap; + +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.command.ActiveMQQueue; +import org.json.JSONObject; +import org.springframework.jms.connection.CachingConnectionFactory; +import org.springframework.jms.core.JmsTemplate; + +public class AAIDmaapEventJMSProducer { + + private JmsTemplate jmsTemplate; + + public AAIDmaapEventJMSProducer() { + this.jmsTemplate = new JmsTemplate(); + this.jmsTemplate.setConnectionFactory(new CachingConnectionFactory(new ActiveMQConnectionFactory("tcp://localhost:61446"))); + this.jmsTemplate.setDefaultDestination(new ActiveMQQueue("IN_QUEUE")); + } + + public void sendMessageToDefaultDestination(JSONObject finalJson) { + jmsTemplate.convertAndSend(finalJson.toString()); + CachingConnectionFactory ccf = (CachingConnectionFactory)this.jmsTemplate.getConnectionFactory(); + ccf.destroy(); + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/extensions/AAIExtensionMap.java b/aai-traversal/src/main/java/org/openecomp/aai/extensions/AAIExtensionMap.java new file mode 100644 index 0000000..167dae4 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/extensions/AAIExtensionMap.java @@ -0,0 +1,827 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.extensions; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import org.openecomp.aai.domain.responseMessage.AAIResponseMessages; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.rest.db.DBRequest; +import org.openecomp.aai.rest.db.HttpEntry; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + +public class AAIExtensionMap { + // ======================================================================= + // Attribute | Type + // ======================================================================= + // message | java.lang.String (RW) + // ---------------------------------------------------------------------- + // templateVars | java.lang.ArrayList<String> (RW) + // ----------------------------------------------------------------------- + // preExtException | java.lang.Exception (RW) + // ----------------------------------------------------------------------- + // preExtErrorCallback | java.lang.reflect.Method (RW) + // ----------------------------------------------------------------------- + // postExtException | java.lang.Exception (RW) + // ----------------------------------------------------------------------- + // postExtErrorCallback | java.lang.reflect.Method (RW) + // ----------------------------------------------------------------------- + // servletRequest | javax.servlet.http.HttpServletRequest (RO) + // ----------------------------------------------------------------------- + // headers | javax.ws.rs.core.HttpHeaders (RO) + // ----------------------------------------------------------------------- + // objFromRequestType | String (ex. ?org.openecomp.aai.domain.yang.Vce?) (RO) + // ----------------------------------------------------------------------- + // objFromRequest | $TYPE {ObjFromRequestType) (RO) + // ----------------------------------------------------------------------- + // preExtFailOnError | java.lang.Boolean (RW) + // ----------------------------------------------------------------------- + // postExtFailOnError | java.lang.Boolean (RW) + // ----------------------------------------------------------------------- + // preExtSkipErrorCallback | java.lang.Boolean (RW) + // ----------------------------------------------------------------------- + // postExtSkipErrorCallback | java.lang.Boolean (RW) + // ----------------------------------------------------------------------- + // graph | com.thinkaurelius.titan.core.TitanGraph (RW) + // ----------------------------------------------------------------------- + // objectFromResponse | Object + // ----------------------------------------------------------------------- + // precheckAddedList | java.util.HashMap + // ----------------------------------------------------------------------- + // precheckResponseMessages | org.openecomp.aai.extensions.AAIResponseMessages + // ======================================================================= + + private String message; + private ArrayList<String> templateVars; + private Exception preExtException; + private Exception postExtException; + private Method preExtErrorCallback; + private Method postExtErrorCallback; + private HttpServletRequest servletRequest; + private HttpHeaders httpHeaders; + private String objectFromRequestType; + private Object objectFromRequest; + private boolean preExtFailOnError = true; + private boolean postExtFailOnError = true; + private boolean preExtSkipErrorCallback = true; + private boolean postExtSkipErrorCallback = true; + private String fromAppId; + private String transId; + private TitanTransaction graph; + private Object objectFromResponse; + private HashMap<String, Object> lookupHashMap; + private HashMap<String, ArrayList<String>> precheckAddedList; + private AAIResponseMessages precheckResponseMessages; + private HashMap<String, Object> topology; + private HashMap<String, TitanVertex> vertexCache; + private String baseObject; + private String namespace; + private String fullResourceName; + private String topObjectFullResourceName; + private String uri; + private String notificationUri; + private String apiVersion; + private long startTime; + private long checkpointTime; + private DynamicJAXBContext jaxbContext; + private String objectFromResponseType; + private String eventAction; + private TransactionalGraphEngine dbEngine; + private Loader loader; + private UriInfo uriInfo; + private DBRequest dbRequest; + private HttpEntry httpEntry; + /** + * Sets the message. + * + * @param _message the new message + */ + public void setMessage(String _message) { + this.message = _message; + } + + /** + * Sets the template vars. + * + * @param _templateVars the new template vars + */ + public void setTemplateVars(ArrayList<String> _templateVars) { + this.templateVars = _templateVars; + } + + /** + * Sets the pre ext exception. + * + * @param _exception the new pre ext exception + */ + public void setPreExtException(Exception _exception) { + this.preExtException = _exception; + } + + /** + * Sets the pre ext error callback. + * + * @param _errorCallback the new pre ext error callback + */ + public void setPreExtErrorCallback(Method _errorCallback) { + this.preExtErrorCallback = _errorCallback; + } + + /** + * Sets the post ext exception. + * + * @param _exception the new post ext exception + */ + public void setPostExtException(Exception _exception) { + this.postExtException = _exception; + } + + /** + * Sets the post ext error callback. + * + * @param _errorCallback the new post ext error callback + */ + public void setPostExtErrorCallback(Method _errorCallback) { + this.postExtErrorCallback = _errorCallback; + } + + /** + * Sets the servlet request. + * + * @param _httpServletRequest the new servlet request + */ + public void setServletRequest(HttpServletRequest _httpServletRequest) { + this.servletRequest = _httpServletRequest; + } + + /** + * Sets the http headers. + * + * @param _httpHeaders the new http headers + */ + public void setHttpHeaders(HttpHeaders _httpHeaders) { + this.httpHeaders = _httpHeaders; + } + + /** + * Sets the object from request type. + * + * @param _objectFromRequestType the new object from request type + */ + public void setObjectFromRequestType(String _objectFromRequestType) { + this.objectFromRequestType = _objectFromRequestType; + } + + /** + * Sets the object from request. + * + * @param _objectFromRequest the new object from request + */ + public void setObjectFromRequest(Object _objectFromRequest) { + this.objectFromRequest = _objectFromRequest; + } + + /** + * Sets the object from response type. + * + * @param resourceClassName the new object from response type + */ + public void setObjectFromResponseType(String resourceClassName) { + // TODO Auto-generated method stub + this.objectFromResponseType = resourceClassName; + } + + /** + * Gets the object from response type. + * + * @return the object from response type + */ + public String getObjectFromResponseType() { + // TODO Auto-generated method stub + return this.objectFromResponseType; + } + + /** + * Sets the pre ext fail on error. + * + * @param _failOnError the new pre ext fail on error + */ + public void setPreExtFailOnError(boolean _failOnError) { + this.preExtFailOnError = _failOnError; + } + + /** + * Sets the post ext fail on error. + * + * @param _failOnError the new post ext fail on error + */ + public void setPostExtFailOnError(boolean _failOnError) { + this.postExtFailOnError = _failOnError; + } + + /** + * Gets the message. + * + * @return the message + */ + public String getMessage() { + return this.message; + } + + /** + * Gets the template vars. + * + * @return the template vars + */ + public ArrayList<String> getTemplateVars() { + if (this.templateVars == null) { + this.templateVars = new ArrayList<String>(); + } + return this.templateVars; + } + + /** + * Gets the pre ext exception. + * + * @return the pre ext exception + */ + public Exception getPreExtException() { + return this.preExtException; + } + + /** + * Gets the pre ext error callback. + * + * @return the pre ext error callback + */ + public Method getPreExtErrorCallback() { + return this.preExtErrorCallback; + } + + /** + * Gets the post ext exception. + * + * @return the post ext exception + */ + public Exception getPostExtException() { + return this.postExtException; + } + + /** + * Gets the post ext error callback. + * + * @return the post ext error callback + */ + public Method getPostExtErrorCallback() { + return this.postExtErrorCallback; + } + + /** + * Gets the http servlet request. + * + * @return the http servlet request + */ + public HttpServletRequest getHttpServletRequest() { + return this.servletRequest; + } + + /** + * Gets the http headers. + * + * @return the http headers + */ + public HttpHeaders getHttpHeaders() { + return this.httpHeaders; + } + + /** + * Gets the object from request type. + * + * @return the object from request type + */ + public String getObjectFromRequestType() { + return this.objectFromRequestType; + } + + /** + * Gets the object from request. + * + * @return the object from request + */ + public Object getObjectFromRequest() { + return this.objectFromRequest; + } + + /** + * Gets the pre ext fail on error. + * + * @return the pre ext fail on error + */ + public boolean getPreExtFailOnError() { + return this.preExtFailOnError; + } + + /** + * Gets the post ext fail on error. + * + * @return the post ext fail on error + */ + public boolean getPostExtFailOnError() { + return this.postExtFailOnError; + } + + /** + * Gets the from app id. + * + * @return the from app id + */ + public String getFromAppId() { + return this.fromAppId; + } + + /** + * Sets the from app id. + * + * @param fromAppId the new from app id + */ + public void setFromAppId(String fromAppId) { + this.fromAppId = fromAppId; + } + + /** + * Gets the trans id. + * + * @return the trans id + */ + public String getTransId() { + return this.transId; + } + + /** + * Sets the trans id. + * + * @param transId the new trans id + */ + public void setTransId(String transId) { + this.transId = transId; + } + + /** + * Gets the pre ext skip error callback. + * + * @return the pre ext skip error callback + */ + public boolean getPreExtSkipErrorCallback() { + return preExtSkipErrorCallback; + } + + /** + * Sets the pre ext skip error callback. + * + * @param preExtSkipErrorCallback the new pre ext skip error callback + */ + public void setPreExtSkipErrorCallback(boolean preExtSkipErrorCallback) { + this.preExtSkipErrorCallback = preExtSkipErrorCallback; + } + + /** + * Gets the post ext skip error callback. + * + * @return the post ext skip error callback + */ + public boolean getPostExtSkipErrorCallback() { + return postExtSkipErrorCallback; + } + + /** + * Sets the post ext skip error callback. + * + * @param postExtSkipErrorCallback the new post ext skip error callback + */ + public void setPostExtSkipErrorCallback(boolean postExtSkipErrorCallback) { + this.postExtSkipErrorCallback = postExtSkipErrorCallback; + } + + /** + * Gets the graph. + * + * @return the graph + */ + public TitanTransaction getGraph() { + return graph; + } + + /** + * Sets the graph. + * + * @param graph the new graph + */ + public void setGraph(TitanTransaction graph) { + this.graph = graph; + } + + /** + * Gets the object from response. + * + * @return the object from response + */ + public Object getObjectFromResponse() { + return objectFromResponse; + } + + /** + * Sets the object from response. + * + * @param objectFromResponse the new object from response + */ + public void setObjectFromResponse(Object objectFromResponse) { + this.objectFromResponse = objectFromResponse; + } + + /** + * Gets the lookup hash map. + * + * @return the lookup hash map + */ + public HashMap<String, Object> getLookupHashMap() { + if (this.lookupHashMap == null) { + this.lookupHashMap = new HashMap<String, Object>(); + } + return this.lookupHashMap; + } + + /** + * Sets the lookup hash map. + * + * @param lookupHashMap the lookup hash map + */ + public void setLookupHashMap(HashMap<String, Object> lookupHashMap) { + this.lookupHashMap = lookupHashMap; + } + + /** + * Gets the precheck added list. + * + * @return the precheck added list + */ + public HashMap<String, ArrayList<String>> getPrecheckAddedList() { + if (this.precheckAddedList == null) { + this.precheckAddedList = new HashMap<String, ArrayList<String>>(); + } + return precheckAddedList; + } + + /** + * Sets the precheck added list. + * + * @param precheckAddedList the precheck added list + */ + public void setPrecheckAddedList(HashMap<String, ArrayList<String>> precheckAddedList) { + this.precheckAddedList = precheckAddedList; + } + + /** + * Gets the precheck response messages. + * + * @return the precheck response messages + */ + public AAIResponseMessages getPrecheckResponseMessages() { + if (this.precheckResponseMessages == null) { + this.precheckResponseMessages = new AAIResponseMessages(); + } + return precheckResponseMessages; + } + + /** + * Sets the precheck response messages. + * + * @param precheckResponseData the new precheck response messages + */ + public void setPrecheckResponseMessages(AAIResponseMessages precheckResponseData) { + this.precheckResponseMessages = precheckResponseData; + } + + /** + * Gets the topology. + * + * @return the topology + */ + public HashMap<String, Object> getTopology() { + if (this.topology == null) { + this.topology = new HashMap<String, Object>(); + } + return topology; + } + + /** + * Gets the vertex cache. + * + * @return the vertex cache + */ + public HashMap<String, TitanVertex> getVertexCache() { + if (this.vertexCache == null) { + this.vertexCache = new HashMap<String, TitanVertex>(); + } + return vertexCache; + } + + /** + * Gets the base object. + * + * @return the base object + */ + public String getBaseObject() { + return baseObject; + } + + /** + * Sets the base object. + * + * @param baseObject the new base object + */ + public void setBaseObject(String baseObject) { + this.baseObject = baseObject; + } + + /** + * Gets the namespace. + * + * @return the namespace + */ + public String getNamespace() { + return namespace; + } + + /** + * Sets the namespace. + * + * @param namespace the new namespace + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + /** + * Gets the full resource name. + * + * @return the full resource name + */ + public String getFullResourceName() { + return fullResourceName; + } + + /** + * Sets the full resource name. + * + * @param fullResourceName the new full resource name + */ + public void setFullResourceName(String fullResourceName) { + this.fullResourceName = fullResourceName; + } + + /** + * Gets the top object full resource name. + * + * @return the top object full resource name + */ + public String getTopObjectFullResourceName() { + return topObjectFullResourceName; + } + + /** + * Sets the top object full resource name. + * + * @param topObjectFullResourceName the new top object full resource name + */ + public void setTopObjectFullResourceName(String topObjectFullResourceName) { + this.topObjectFullResourceName = topObjectFullResourceName; + } + + /** + * Gets the uri. + * + * @return the uri + */ + public String getUri() { + return uri; + } + + /** + * Sets the uri. + * + * @param uri the new uri + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Gets the api version. + * + * @return the api version + */ + public String getApiVersion() { + return apiVersion; + } + + /** + * Sets the api version. + * + * @param apiVersion the new api version + */ + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + /** + * Sets the notification uri. + * + * @param uri the new notification uri + */ + public void setNotificationUri(String uri) { + this.notificationUri = uri; + + } + + /** + * Gets the notification uri. + * + * @return the notification uri + */ + public String getNotificationUri() { + return this.notificationUri; + + } + + /** + * Gets the start time. + * + * @return the start time + */ + public long getStartTime() { + return startTime; + } + + /** + * Sets the start time. + * + * @param startTime the new start time + */ + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + /** + * Gets the checkpoint time. + * + * @return the checkpoint time + */ + public long getCheckpointTime() { + return checkpointTime; + } + + /** + * Sets the checkpoint time. + * + * @param checkpointTime the new checkpoint time + */ + public void setCheckpointTime(long checkpointTime) { + this.checkpointTime = checkpointTime; + } + + /** + * Gets the jaxb context. + * + * @return the jaxb context + */ + public DynamicJAXBContext getJaxbContext() { + return jaxbContext; + } + + /** + * Sets the jaxb context. + * + * @param jaxbContext the new jaxb context + */ + public void setJaxbContext(DynamicJAXBContext jaxbContext) { + this.jaxbContext = jaxbContext; + } + + /** + * Sets the event action. + * + * @param eventAction the new event action + */ + public void setEventAction(String eventAction) { + this.eventAction = eventAction; + } + + /** + * Gets the event action. + * + * @return the event action + */ + public String getEventAction() { + return this.eventAction; + } + + /** + * Gets the transactional graph engine. + * + * @return the transactional graph engine + */ + public TransactionalGraphEngine getTransactionalGraphEngine() { + return this.dbEngine; + + } + + /** + * Sets the transactional graph engine. + * + * @param dbEngine the new transactional graph engine + */ + public void setTransactionalGraphEngine(TransactionalGraphEngine dbEngine) { + this.dbEngine = dbEngine; + + } + + /** + * Gets the loader. + * + * @return the loader + */ + public Loader getLoader() { + return loader; + } + + /** + * Sets the loader. + * + * @param loader the new loader + */ + public void setLoader(Loader loader) { + this.loader = loader; + } + + /** + * Gets the uri info. + * + * @return the uri info + */ + public UriInfo getUriInfo() { + return uriInfo; + } + + /** + * Sets the uri info. + * + * @param uriInfo the new uri info + */ + public void setUriInfo(UriInfo uriInfo) { + this.uriInfo = uriInfo; + } + + public DBRequest getDbRequest() { + return dbRequest; + } + + public void setDbRequest(DBRequest dbRequest) { + this.dbRequest = dbRequest; + } + + public HttpEntry getHttpEntry() { + return httpEntry; + } + + public void setHttpEntry(HttpEntry httpEntry) { + this.httpEntry = httpEntry; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/extensions/ExtensionController.java b/aai-traversal/src/main/java/org/openecomp/aai/extensions/ExtensionController.java new file mode 100644 index 0000000..09cfe50 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/extensions/ExtensionController.java @@ -0,0 +1,145 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.extensions; + +import java.lang.reflect.Method; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.util.AAIConfig; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +public class ExtensionController { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ExtensionController.class); + + /** + * Run extension. + * + * @param apiVersion the api version + * @param namespace the namespace + * @param resourceName the resource name + * @param methodName the method name + * @param aaiExtMap the aai ext map + * @param isPreExtension the is pre extension + * @throws AAIException the AAI exception + */ + public void runExtension(String apiVersion, String namespace, + String resourceName, String methodName, AAIExtensionMap aaiExtMap, + boolean isPreExtension) throws AAIException { + String extensionClassName = "org.openecomp.aai.extensions." + + apiVersion.toLowerCase() + "." + namespace + "." + + resourceName + "Extension"; + String defaultErrorCallback = resourceName + "ExtensionErrorCallback"; + + String configOption = "aai.extensions." + apiVersion.toLowerCase() + + "." + namespace.toLowerCase() + "." + + resourceName.toLowerCase() + ".enabled"; + + try { + + String extensionEnabled = AAIConfig.get(configOption, "true"); + if (extensionEnabled.equalsIgnoreCase("false")) { + return; + } + + Class<?> clazz = Class.forName(extensionClassName); + + Method extension = clazz.getMethod(methodName, + new Class[] { AAIExtensionMap.class }); + if (extension != null) { + + Object ret = extension.invoke(clazz.newInstance(), aaiExtMap); + + if (ret instanceof Integer) { + Exception e = null; + + if (isPreExtension == true) { + e = aaiExtMap.getPreExtException(); + } else { + e = aaiExtMap.getPostExtException(); + } + + boolean failOnError = true; + if (isPreExtension == true) { + failOnError = aaiExtMap.getPreExtFailOnError(); + } else { + failOnError = aaiExtMap.getPostExtFailOnError(); + } + + if (e != null) { + boolean handleException = true; + if (isPreExtension == true) { + if (aaiExtMap.getPreExtSkipErrorCallback() == true) { + handleException = false; + } + } else { + if (aaiExtMap.getPostExtSkipErrorCallback() == true) { + handleException = false; + } + } + if (handleException == true) { + Method errorCallback = null; + if (isPreExtension == true) { + errorCallback = aaiExtMap + .getPreExtErrorCallback(); + } else { + errorCallback = aaiExtMap + .getPostExtErrorCallback(); + } + + if (errorCallback != null) { + errorCallback.invoke(clazz.newInstance(), + aaiExtMap); + } else { + Method defaultErrorCallbackExtension = clazz + .getMethod( + defaultErrorCallback, + new Class[] { AAIExtensionMap.class }); + defaultErrorCallbackExtension.invoke( + clazz.newInstance(), aaiExtMap); + } + } + } + + if (failOnError == true && e != null) { + throw e; + } else if (failOnError == false && e != null) { // in this + // case, we + // just note + // the error + // without + // stopping + LOGGER.warn("Error while processing extension - " + aaiExtMap.getMessage()); + } + } + } + } catch (ClassNotFoundException ex) { + LOGGER.debug("Extension class not found: " + extensionClassName + ", method: " + methodName + "."); + } catch (NoSuchMethodException e) { + LOGGER.debug("Method " + methodName + " does not exist for class " + extensionClassName); + } catch (AAIException e) { + throw e; + } catch (Exception e) { + throw new AAIException("AAI_5105", e); + } + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAIHeaderProperties.java b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAIHeaderProperties.java new file mode 100644 index 0000000..4798d90 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAIHeaderProperties.java @@ -0,0 +1,26 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.interceptors; + +public class AAIHeaderProperties { + + public static final String REQUEST_CONTEXT = "aai-request-context"; +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSInInterceptor.java b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSInInterceptor.java new file mode 100644 index 0000000..80127d0 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSInInterceptor.java @@ -0,0 +1,283 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.interceptors; + +import java.io.InputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.ws.rs.core.MediaType; + +import org.apache.commons.io.IOUtils; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.interceptor.LoggingMessage; +import org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor; +import org.apache.cxf.message.Message; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.rest.util.EchoResponse; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import org.openecomp.aai.util.HbaseSaltPrefixer; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +public class AAILogJAXRSInInterceptor extends JAXRSInInterceptor { + + protected final String COMPONENT = "aairest"; + protected final String CAMEL_REQUEST ="CamelHttpUrl"; + private static final Pattern uuidPattern = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"); + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAILogJAXRSInInterceptor.class); + + /** + * {@inheritDoc} + */ + public void handleMessage(Message message) { + + boolean go = false; + String uri = null; + String query = null; + try { + + uri = (String)message.get(CAMEL_REQUEST); + if (uri != null) { + query = (String)message.get(Message.QUERY_STRING); + } + + if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_INTERCEPTOR).equalsIgnoreCase("true") && + AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_ENABLED).equalsIgnoreCase("true")) { + go = true; + message.getExchange().put("AAI_LOGGING_HBASE_ENABLED", 1); + if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_LOGREQUEST).equalsIgnoreCase("true") ) { + message.getExchange().put("AAI_LOGGING_HBASE_LOGREQUEST", 1); + } + if (AAIConfig.get(AAIConstants.AAI_LOGGING_HBASE_LOGRESPONSE).equalsIgnoreCase("true") ) { + message.getExchange().put("AAI_LOGGING_HBASE_LOGRESPONSE", 1); + } + } + if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_ENABLED).equalsIgnoreCase("true") ) { + go = true; + message.getExchange().put("AAI_LOGGING_TRACE_ENABLED", 1); + if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_LOGREQUEST).equalsIgnoreCase("true") ) { + message.getExchange().put("AAI_LOGGING_TRACE_LOGREQUEST", 1); + } + if (AAIConfig.get(AAIConstants.AAI_LOGGING_TRACE_LOGRESPONSE).equalsIgnoreCase("true") ) { + message.getExchange().put("AAI_LOGGING_TRACE_LOGRESPONSE", 1); + } + } + } catch (AAIException e1) { + ErrorLogHelper.logException(e1); + } + + if (uri.contains(EchoResponse.echoPath)) { + // if it's a health check, we don't want to log ANYTHING if it's a lightweight one + if (query == null) { + if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED")) { + message.getExchange().remove("AAI_LOGGING_HBASE_ENABLED"); + } + if (message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { + message.getExchange().remove("AAI_LOGGING_TRACE_ENABLED"); + } + go = false; + } + } + else if (uri.contains("/translog/")) { + // if it's a translog query, we don't want to log the responses + if (message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGRESPONSE")) { + message.getExchange().remove("AAI_LOGGING_HBASE_LOGRESPONSE"); + } + if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGRESPONSE")) { + message.getExchange().remove("AAI_LOGGING_TRACE_LOGRESPONSE"); + } + } + + if (go == false) { // there's nothing to do + return; + } + + // DONE: get a TXID based on hostname, time (YYYYMMDDHHMMSSMILLIS, and LoggingMessage.nextId(); 20150326145301-1 + String now = genDate(); + + message.getExchange().put("AAI_RQST_TM", now); + + String id = (String)message.getExchange().get(LoggingMessage.ID_KEY); + + String fullId = null; + try { + if (id == null) { + id = LoggingMessage.nextId(); + } + fullId = AAIConfig.get(AAIConstants.AAI_NODENAME) + "-" + now + "-" + id; + fullId = HbaseSaltPrefixer.getInstance().prependSalt(fullId); + message.getExchange().put(LoggingMessage.ID_KEY, fullId); + } catch (AAIException e1) { + LOGGER.debug("config problem", e1); + } + + if (fullId == null) { + fullId = now + "-" + id; + fullId = HbaseSaltPrefixer.getInstance().prependSalt(fullId); + } + message.put(LoggingMessage.ID_KEY, fullId); + final LoggingMessage buffer = new LoggingMessage("Message", fullId); + + Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE); + if (responseCode != null) { + buffer.getResponseCode().append(responseCode); + } + + String encoding = (String)message.get(Message.ENCODING); + + if (encoding != null) { + buffer.getEncoding().append(encoding); + } + String httpMethod = (String)message.get(Message.HTTP_REQUEST_METHOD); + if (httpMethod != null) { + buffer.getHttpMethod().append(httpMethod); + } + + String ct = (String)message.get(Message.CONTENT_TYPE); + if (ct != null) { + if ("*/*".equals(ct)) { + message.put(Message.CONTENT_TYPE, MediaType.APPLICATION_JSON); + ct = MediaType.APPLICATION_JSON; + } + buffer.getContentType().append(ct); + + } + Object headers = message.get(Message.PROTOCOL_HEADERS); + if (headers != null) { + buffer.getHeader().append(headers); + + Map<String, List<String>> headersList = CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS)); + String transId = ""; + List<String> xt = headersList.get("X-TransactionId"); + String newTransId = transId; + boolean missingTransId = false; + boolean replacedTransId = false; + String logMsg = null; + if (xt != null) { + for (String transIdValue : xt) { + transId = transIdValue; + } + Matcher matcher = uuidPattern.matcher(transId); + if (!matcher.find()) { + replacedTransId = true; + // check if there's a colon, and check the first group? + if (transId.contains(":")) { + String[] uuidParts = transId.split(":"); + Matcher matcher2 = uuidPattern.matcher(uuidParts[0]); + if (matcher2.find()) { + newTransId = uuidParts[0]; + } else { + // punt, we tried to find it, it has a colon but no UUID-1 + newTransId = UUID.randomUUID().toString(); + } + } else { + newTransId = UUID.randomUUID().toString(); + } + } + } else { + newTransId = UUID.randomUUID().toString(); + missingTransId = true; + } + + if (missingTransId || replacedTransId) { + List<String> txList = new ArrayList<String>(); + txList.add(newTransId); + headersList.put("X-TransactionId", txList); + if (missingTransId) { + logMsg = "Missing requestID. Assigned " + newTransId; + } else if (replacedTransId) { + logMsg = "Replaced invalid requestID of " + transId + " Assigned " + newTransId; + } + } + + List<String> contentType = headersList.get("Content-Type"); + if (contentType == null) { + ct = (String)message.get(Message.CONTENT_TYPE); + headersList.put(Message.CONTENT_TYPE, Collections.singletonList(ct)); + } + + LOGGER.auditEvent("REST " + httpMethod + " " + ((query != null)? uri+"?"+query : uri) + " HbaseTxId=" + fullId); + LOGGER.info(logMsg); + } + + + if (uri != null) { + buffer.getAddress().append(uri); + if (query != null) { + buffer.getAddress().append("?").append(query); + } + } + + InputStream is = message.getContent(InputStream.class); + if (is != null) { + try { + String currentPayload = IOUtils.toString(is, "UTF-8"); + IOUtils.closeQuietly(is); + buffer.getPayload().append(currentPayload); + is = IOUtils.toInputStream(currentPayload, "UTF-8"); + message.setContent(InputStream.class, is); + IOUtils.closeQuietly(is); + } catch (Exception e) { + // It's ok to not have request input content + // throw new Fault(e); + } + } + + // this will be saved in the message exchange, and can be pulled out later... + message.getExchange().put(fullId + "_REQUEST", buffer.toString()); + } + + /** + * Gen date. + * + * @param aaiLogger the aai logger + * @param logline the logline + * @return the string + */ + protected String genDate() { + Date date = new Date(); + DateFormat formatter = null; + try { + formatter = new SimpleDateFormat(AAIConfig.get(AAIConstants.HBASE_TABLE_TIMESTAMP_FORMAT)); + } catch (AAIException ex) { + ErrorLogHelper.logException(ex); + } finally { + if (formatter == null) { + formatter = new SimpleDateFormat("YYMMdd-HH:mm:ss:SSS"); + } + } + + return formatter.format(date); + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSOutInterceptor.java b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSOutInterceptor.java new file mode 100644 index 0000000..0f5e457 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/AAILogJAXRSOutInterceptor.java @@ -0,0 +1,323 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.interceptors; + +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.interceptor.LoggingMessage; +import org.apache.cxf.io.CacheAndWriteOutputStream; +import org.apache.cxf.io.CachedOutputStream; +import org.apache.cxf.io.CachedOutputStreamCallback; +import org.apache.cxf.jaxrs.interceptor.JAXRSOutInterceptor; +import org.apache.cxf.message.Message; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.util.AAIConfig; +import org.openecomp.aai.util.AAIConstants; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +// right after the request is complete, there may be content +public class AAILogJAXRSOutInterceptor extends JAXRSOutInterceptor { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAILogJAXRSOutInterceptor.class); + + protected final String COMPONENT = "aairest"; + protected final String CAMEL_REQUEST = "CamelHttpUrl"; + + /** + * {@inheritDoc} + */ + public void handleMessage(Message message) { + + String fullId = (String) message.getExchange().get(LoggingMessage.ID_KEY); + + Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>) message.get(Message.PROTOCOL_HEADERS)); + if (headers == null) { + headers = new HashMap<String, List<String>>(); + } + + headers.put("X-AAI-TXID", Collections.singletonList(fullId)); + message.put(Message.PROTOCOL_HEADERS, headers); + + Message outMessage = message.getExchange().getOutMessage(); + final OutputStream os = outMessage.getContent(OutputStream.class); + if (os == null) { + return; + } + + // we only want to register the callback if there is good reason for it. + if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED") || message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { + + final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os); + message.setContent(OutputStream.class, newOut); + newOut.registerCallback(new LoggingCallback(message, os)); + } + + } + + class LoggingCallback implements CachedOutputStreamCallback { + + private final Message message; + private final OutputStream origStream; + + public LoggingCallback(final Message msg, final OutputStream os) { + this.message = msg; + this.origStream = os; + } + + public void onFlush(CachedOutputStream cos) { + + } + + public void onClose(CachedOutputStream cos) { + + String getValue = ""; + String postValue = ""; + String logValue = ""; + + try { + logValue = AAIConfig.get("aai.transaction.logging"); + getValue = AAIConfig.get("aai.transaction.logging.get"); + postValue = AAIConfig.get("aai.transaction.logging.post"); + } catch (AAIException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED") && !message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { + return; + } + + String fullId = (String) message.getExchange().get(LoggingMessage.ID_KEY); + + Message inMessage = message.getExchange().getInMessage(); + String transId = null; + String fromAppId = null; + + Map<String, List<String>> headersList = CastUtils.cast((Map<?, ?>) inMessage.get(Message.PROTOCOL_HEADERS)); + if (headersList != null) { + List<String> xt = headersList.get("X-TransactionId"); + if (xt != null) { + for (String transIdValue : xt) { + transId = transIdValue; + } + } + List<String> fa = headersList.get("X-FromAppId"); + if (fa != null) { + for (String fromAppIdValue : fa) { + + fromAppId = fromAppIdValue; + } + } + } + + String httpMethod = (String) inMessage.get(Message.HTTP_REQUEST_METHOD); + + String uri = (String) inMessage.get(CAMEL_REQUEST); + String fullUri = uri; + if (uri != null) { + String query = (String) message.get(Message.QUERY_STRING); + if (query != null) { + fullUri = uri + "?" + query; + } + } + + String request = (String) message.getExchange().get(fullId + "_REQUEST"); + + Message outMessage = message.getExchange().getOutMessage(); + + final LoggingMessage buffer = new LoggingMessage("OUTMessage", fullId); + + // should we check this, and make sure it's not an error? + Integer responseCode = (Integer) outMessage.get(Message.RESPONSE_CODE); + if (responseCode == null) { + responseCode = 200; // this should never happen, but just in + // case we don't get one + } + buffer.getResponseCode().append(responseCode); + + String encoding = (String) outMessage.get(Message.ENCODING); + + if (encoding != null) { + buffer.getEncoding().append(encoding); + } + + String ct = (String) outMessage.get(Message.CONTENT_TYPE); + if (ct != null) { + buffer.getContentType().append(ct); + } + + Object headers = outMessage.get(Message.PROTOCOL_HEADERS); + if (headers != null) { + buffer.getHeader().append(headers); + } + + Boolean ss = false; + if (responseCode >= 200 && responseCode <= 299) { + ss = true; + } + String response = buffer.toString(); + + // this should have been set by the in interceptor + String rqstTm = (String) message.getExchange().get("AAI_RQST_TM"); + + // just in case it wasn't, we'll put this here. not great, but it'll + // have a val. + if (rqstTm == null) { + rqstTm = genDate(); + } + + + String respTm = genDate(); + + try { + String actualRequest = request; + StringBuilder builder = new StringBuilder(); + cos.writeCacheTo(builder, 100000); + // here comes my xml: + String payload = builder.toString(); + + String actualResponse = response; + if (payload == null) { + + } else { + actualResponse = response + payload; + } + + // we only log to AAI log if it's eanbled in the config props + // file + if (message.getExchange().containsKey("AAI_LOGGING_TRACE_ENABLED")) { + + if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGREQUEST")) { + + // strip newlines from request + String traceRequest = actualRequest; + traceRequest = traceRequest.replace("\n", " "); + traceRequest = traceRequest.replace("\r", ""); + traceRequest = traceRequest.replace("\t", ""); + LOGGER.debug(traceRequest); + } + if (message.getExchange().containsKey("AAI_LOGGING_TRACE_LOGRESPONSE")) { + // strip newlines from response + String traceResponse = actualResponse; + traceResponse = traceResponse.replace("\n", " "); + traceResponse = traceResponse.replace("\r", ""); + traceResponse = traceResponse.replace("\t", ""); + + LOGGER.debug(traceResponse); + } + } + + // we only log to HBASE if it's enabled in the config props file + // TODO: pretty print XML/JSON. we might need to get the payload + // and envelope seperately + if (message.getExchange().containsKey("AAI_LOGGING_HBASE_ENABLED")) { + if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGREQUEST")) { + actualRequest = "loggingDisabled"; + } + if (!message.getExchange().containsKey("AAI_LOGGING_HBASE_LOGRESPONSE")) { + actualResponse = "loggingDisabled"; + } + + LOGGER.debug("action={}, urlin={}, HbTransId={}", httpMethod, fullUri, fullId); + + if (logValue.equals("false")) { + } else if (getValue.equals("false") && httpMethod.equals("GET")) { + } else if (postValue.equals("false") && httpMethod.equals("POST")) { + } else { + putTransaction(transId, responseCode.toString(), rqstTm, respTm, fromAppId + ":" + transId, fullUri, httpMethod, request, response, actualResponse); + + } + } + } catch (Exception ex) { + // ignore + } + + message.setContent(OutputStream.class, origStream); + + LOGGER.auditEvent("HTTP Response Code: {}", responseCode.toString()); + } + + } + + protected String genDate() { + Date date = new Date(); + DateFormat formatter = null; + try { + formatter = new SimpleDateFormat(AAIConfig.get(AAIConstants.HBASE_TABLE_TIMESTAMP_FORMAT)); + } catch (AAIException ex) { + ErrorLogHelper.logException(ex); + } finally { + if (formatter == null) { + formatter = new SimpleDateFormat("YYMMdd-HH:mm:ss:SSS"); + } + } + return formatter.format(date); + } + + public String putTransaction(String tid, String status, String rqstTm, String respTm, String srcId, String rsrcId, String rsrcType, String rqstBuf, String respBuf, String actualResponse) { + String tm = null; + String fromAppId = srcId.substring(0, srcId.indexOf(':')); + String transId = srcId.substring(srcId.indexOf(':') + 1); + + if (tid == null || "".equals(tid)) { + Date date = new Date(); + DateFormat formatter = null; + try { + formatter = new SimpleDateFormat(AAIConfig.get(AAIConstants.HBASE_TABLE_TIMESTAMP_FORMAT)); + } catch (Exception e) { + formatter = new SimpleDateFormat("YYYYMMdd-HH:mm:ss:SSS"); + } + tm = formatter.format(date); + tid = tm + "-"; + } + + String htid = tid; + + if (rqstTm == null || "".equals(rqstTm)) { + rqstTm = tm; + } + + if (respTm == null || "".equals(respTm)) { + respTm = tm; + } + + try { + LOGGER.debug(" transactionId:" + tid + " status: " + status + " rqstDate: " + rqstTm + " respDate: " + respTm + " sourceId: " + srcId + " resourceId: " + + rsrcId + " resourceType: " + rsrcType + " payload rqstBuf: " + rqstBuf + " payload respBuf: " + respBuf + " Payload Error Messages: " + actualResponse); + return htid; + } catch (Exception e) { + ErrorLogHelper.logError("AAI_4000", "Exception updating HBase:"); + return htid; + } + + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PostAaiAjscInterceptor.java b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PostAaiAjscInterceptor.java new file mode 100644 index 0000000..3a4f899 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PostAaiAjscInterceptor.java @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.interceptors; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.aai.logging.LoggingContext; +import org.openecomp.aai.logging.LoggingContext.StatusCode; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import ajsc.beans.interceptors.AjscInterceptor; + +public class PostAaiAjscInterceptor implements AjscInterceptor { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(PostAaiAjscInterceptor.class); + + private static class LazyAaiAjscInterceptor { + public static final PostAaiAjscInterceptor INSTANCE = new PostAaiAjscInterceptor(); + } + + public static PostAaiAjscInterceptor getInstance() { + return LazyAaiAjscInterceptor.INSTANCE; + } + + @Override + public boolean allowOrReject(HttpServletRequest req, HttpServletResponse resp, Map<?, ?> paramMap) + throws Exception { + final String responseCode = LoggingContext.responseCode(); + + if (responseCode != null && responseCode.startsWith("ERR.")) { + LoggingContext.statusCode(StatusCode.ERROR); + LOGGER.error(req.getRequestURL() + " call failed with responseCode=" + responseCode); + } else { + LoggingContext.statusCode(StatusCode.COMPLETE); + LOGGER.info(req.getRequestURL() + " call succeeded"); + } + + LoggingContext.clear(); + return true; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PreAaiAjscInterceptor.java b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PreAaiAjscInterceptor.java new file mode 100644 index 0000000..a2c56d0 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/interceptors/PreAaiAjscInterceptor.java @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.interceptors; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.aai.logging.LoggingContext; + +import ajsc.beans.interceptors.AjscInterceptor; + +public class PreAaiAjscInterceptor implements AjscInterceptor { + + private static class LazyAaiAjscInterceptor { + public static final PreAaiAjscInterceptor INSTANCE = new PreAaiAjscInterceptor(); + } + + public static PreAaiAjscInterceptor getInstance() { + return LazyAaiAjscInterceptor.INSTANCE; + } + + @Override + public boolean allowOrReject(HttpServletRequest req, HttpServletResponse resp, Map<?, ?> paramMap) + throws Exception { + + LoggingContext.init(); + + LoggingContext.requestId(req.getHeader("X-TransactionId")); + LoggingContext.partnerName(req.getHeader("X-FromAppId")); + LoggingContext.serviceName(req.getMethod() + " " + req.getRequestURI().toString()); + + return true; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/ExceptionHandler.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/ExceptionHandler.java new file mode 100644 index 0000000..f1a6f47 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/ExceptionHandler.java @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.logging.ErrorLogHelper; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.sun.istack.SAXParseException2; + +/** + * The Class ExceptionHandler. + */ +@Provider +public class ExceptionHandler implements ExceptionMapper<Exception> { + + @Context + private HttpServletRequest request; + + @Context + private HttpHeaders headers; + + /** + * @{inheritDoc} + */ + @Override + public Response toResponse(Exception exception) { + + Response response = null; + ArrayList<String> templateVars = new ArrayList<String>(); + + //the general case is that cxf will give us a WebApplicationException + //with a linked exception + if (exception instanceof WebApplicationException) { + WebApplicationException e = (WebApplicationException) exception; + if (e.getCause() != null) { + if (e.getCause() instanceof SAXParseException2) { + templateVars.add("UnmarshalException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + } + } else if (exception instanceof JsonParseException) { + //jackson does it differently so we get the direct JsonParseException + templateVars.add("JsonParseException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } else if (exception instanceof JsonMappingException) { + //jackson does it differently so we get the direct JsonParseException + templateVars.add("JsonMappingException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + + // it didn't get set above, we wrap a general fault here + if (response == null) { + + Exception actual_e = exception; + if (exception instanceof WebApplicationException) { + WebApplicationException e = (WebApplicationException) exception; + response = e.getResponse(); + } else { + templateVars.add(request.getMethod()); + templateVars.add("unknown"); + AAIException ex = new AAIException("AAI_4000", actual_e); + List<MediaType> mediaTypes = headers.getAcceptableMediaTypes(); + int setError = 0; + + for (MediaType mediaType : mediaTypes) { + if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) { + response = Response + .status(400) + .type(MediaType.APPLICATION_XML_TYPE) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + setError = 1; + } + } + if (setError == 0) { + response = Response + .status(400) + .type(MediaType.APPLICATION_JSON_TYPE) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + } + } + return response; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/QueryConsumer.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/QueryConsumer.java new file mode 100644 index 0000000..af1d350 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/QueryConsumer.java @@ -0,0 +1,182 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest; + +import java.net.URI; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Encoded; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.ModelType; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.parsers.query.QueryParser; +import org.openecomp.aai.rest.db.HttpEntry; +import org.openecomp.aai.rest.search.GenericQueryProcessor; +import org.openecomp.aai.rest.search.QueryProcessorType; +import org.openecomp.aai.restcore.HttpMethod; +import org.openecomp.aai.restcore.RESTAPI; +import org.openecomp.aai.restcore.util.URITools; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.engines.QueryStyle; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.queryformats.Format; +import org.openecomp.aai.serialization.queryformats.FormatFactory; +import org.openecomp.aai.serialization.queryformats.Formatter; +import org.openecomp.aai.serialization.queryformats.SubGraphStyle; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +@Path("{version: v9|v1[0]}/query") +public class QueryConsumer extends RESTAPI { + + /** The introspector factory type. */ + private ModelType introspectorFactoryType = ModelType.MOXY; + + private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER; + /** The query style. */ + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + @PUT + @Consumes({ MediaType.APPLICATION_JSON}) + @Produces({ MediaType.APPLICATION_JSON}) + public Response executeQuery(String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("graphson") @QueryParam("format") String queryFormat,@DefaultValue("no_op") @QueryParam("subgraph") String subgraph, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor"); + QueryProcessorType processorType = this.processorType; + Response response = null; + TransactionalGraphEngine dbEngine = null; + try { + + Format format = Format.valueOf(queryFormat); + if (queryProcessor != null) { + processorType = QueryProcessorType.valueOf(queryProcessor); + } + SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph); + JsonParser parser = new JsonParser(); + + JsonObject input = parser.parse(content).getAsJsonObject(); + + JsonElement startElement = input.get("start"); + JsonElement queryElement = input.get("query"); + JsonElement gremlinElement = input.get("gremlin"); + List<URI> startURIs = new ArrayList<>(); + String queryURI = ""; + String gremlin = ""; + + Version version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + dbEngine = httpEntry.getDbEngine(); + + if (startElement != null) { + + if (startElement.isJsonArray()) { + for (JsonElement element : startElement.getAsJsonArray()) { + startURIs.add(new URI(element.getAsString())); + } + } else { + startURIs.add(new URI(startElement.getAsString())); + } + } + + if (queryElement != null) { + queryURI = queryElement.getAsString(); + } + if (gremlinElement != null) { + gremlin = gremlinElement.getAsString(); + } + URI queryURIObj = new URI(queryURI); + GenericQueryProcessor processor = null; + + if (!startURIs.isEmpty()) { + Set<Vertex> vertexSet = new LinkedHashSet<>(); + QueryParser uriQuery; + List<Vertex> vertices; + for (URI startUri : startURIs) { + uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(startUri, URITools.getQueryMap(startUri)); + vertices = uriQuery.getQueryBuilder().toList(); + vertexSet.addAll(vertices); + } + + processor = new GenericQueryProcessor.Builder(dbEngine) + .startFrom(vertexSet).queryFrom(queryURIObj) + .processWith(processorType).create(); + } else if (!queryURI.equals("")){ + processor = new GenericQueryProcessor.Builder(dbEngine) + .queryFrom(queryURIObj) + .processWith(processorType).create(); + } else { + processor = new GenericQueryProcessor.Builder(dbEngine) + .queryFrom(gremlin) + .processWith(processorType).create(); + } + String result = ""; + List<Object> vertices = processor.execute(subGraphStyle); + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + FormatFactory ff = new FormatFactory(httpEntry.getLoader(), serializer); + + Formatter formater = ff.get(format); + + result = formater.output(vertices).toString(); + + response = Response.status(Status.OK) + .type(MediaType.APPLICATION_JSON) + .entity(result).build(); + + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, e); + } catch (Exception e ) { + AAIException ex = new AAIException("AAI_4000", e); + + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, ex); + } finally { + if (dbEngine != null) { + dbEngine.rollback(); + } + } + + return response; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/db/DBRequest.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/db/DBRequest.java new file mode 100644 index 0000000..e7b6858 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/db/DBRequest.java @@ -0,0 +1,251 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.db; + +import java.net.URI; +import java.util.Optional; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.UriInfo; + +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.MarshallerProperties; +import org.openecomp.aai.parsers.query.QueryParser; +import org.openecomp.aai.restcore.HttpMethod; + +/** + * The Class DBRequest. + */ +public class DBRequest { + + private final QueryParser parser; + + private final Introspector introspector; + + private final HttpHeaders headers; + + private final String transactionId; + + private final UriInfo info; + + private final HttpMethod method; + + private final URI uri; + + private final Optional<String> rawRequestContent; + + private final Optional<MarshallerProperties> marshallerProperties; + + + /** + * Instantiates a new DB request. + * + * @param method the method + * @param uri the uri + * @param parser the parser + * @param obj the obj + * @param headers the headers + * @param info the info + * @param transactionId the transaction id + */ + private DBRequest(Builder builder) { + this.method = builder.getMethod(); + this.parser = builder.getParser(); + this.introspector = builder.getIntrospector(); + this.headers = builder.getHeaders(); + this.transactionId = builder.getTransactionId(); + this.info = builder.getInfo(); + this.uri = builder.getUri(); + this.marshallerProperties = builder.getMarshallerProperties(); + this.rawRequestContent = builder.getRawRequestContent(); + } + + /** + * Gets the headers. + * + * @return the headers + */ + public HttpHeaders getHeaders() { + return headers; + } + + + /** + * Gets the transaction id. + * + * @return the transaction id + */ + public String getTransactionId() { + return transactionId; + } + + /** + * Gets the info. + * + * @return the info + */ + public UriInfo getInfo() { + return info; + } + + /** + * Gets the parser. + * + * @return the parser + */ + public QueryParser getParser() { + return parser; + } + + /** + * Gets the introspector. + * + * @return the introspector + */ + public Introspector getIntrospector() { + return introspector; + } + + /** + * Gets the method. + * + * @return the method + */ + public HttpMethod getMethod() { + return method; + } + + /** + * Gets the uri. + * + * @return the uri + */ + public URI getUri() { + return uri; + } + + /** + * Gets the raw content. + * + * @return the raw content + */ + public Optional<String> getRawRequestContent() { + return rawRequestContent; + } + + public Optional<MarshallerProperties> getMarshallerProperties() { + return marshallerProperties; + } + + + + public static class Builder { + + private QueryParser parser = null; + + private Introspector introspector = null; + + private HttpHeaders headers = null; + + private String transactionId = null; + + private UriInfo info = null; + + private HttpMethod method = null; + + private URI uri = null; + + private Optional<MarshallerProperties> marshallerProperties = Optional.empty(); + + private Optional<String> rawRequestContent = Optional.empty(); + /** + * Instantiates a new DB request. + * + * @param method the method + * @param uri the uri + * @param parser the parser + * @param obj the obj + * @param headers the headers + * @param info the info + * @param transactionId the transaction id + */ + public Builder(HttpMethod method, URI uri, QueryParser parser, Introspector obj, HttpHeaders headers, UriInfo info, String transactionId) { + this.method = method; + this.parser = parser; + this.introspector = obj; + this.headers = headers; + this.transactionId = transactionId; + this.info = info; + this.uri = uri; + + } + + public QueryParser getParser() { + return parser; + } + + public Introspector getIntrospector() { + return introspector; + } + + public HttpHeaders getHeaders() { + return headers; + } + + public String getTransactionId() { + return transactionId; + } + + public UriInfo getInfo() { + return info; + } + + public HttpMethod getMethod() { + return method; + } + + public URI getUri() { + return uri; + } + + public Builder customMarshaller(MarshallerProperties properties) { + this.marshallerProperties = Optional.of(properties); + return this; + } + + public Builder rawRequestContent(String content) { + this.rawRequestContent = Optional.of(content); + return this; + } + protected Optional<MarshallerProperties> getMarshallerProperties() { + return marshallerProperties; + } + protected Optional<String> getRawRequestContent() { + return rawRequestContent; + } + public DBRequest build() { + + return new DBRequest(this); + } + + + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/db/HttpEntry.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/db/HttpEntry.java new file mode 100644 index 0000000..60438de --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/db/HttpEntry.java @@ -0,0 +1,672 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.db; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; + +import org.apache.commons.lang.StringUtils; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.javatuples.Pair; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.domain.responseMessage.AAIResponseMessage; +import org.openecomp.aai.domain.responseMessage.AAIResponseMessageDatum; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.extensions.ExtensionController; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.LoaderFactory; +import org.openecomp.aai.introspection.MarshallerProperties; +import org.openecomp.aai.introspection.ModelInjestor; +import org.openecomp.aai.introspection.ModelType; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.parsers.query.QueryParser; +import org.openecomp.aai.parsers.uri.URIToExtensionInformation; +import org.openecomp.aai.rest.ueb.UEBNotification; +import org.openecomp.aai.restcore.HttpMethod; +import org.openecomp.aai.schema.enums.ObjectMetadata; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.engines.QueryStyle; +import org.openecomp.aai.serialization.engines.TitanDBEngine; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.engines.query.QueryEngine; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; +import com.thinkaurelius.titan.core.TitanException; +import com.thinkaurelius.titan.core.TitanTransaction; + +/** + * The Class HttpEntry. + */ +public class HttpEntry { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(HttpEntry.class); + + private final ModelType introspectorFactoryType; + + private final QueryStyle queryStyle; + + private final Version version; + + private final Loader loader; + + private final TransactionalGraphEngine dbEngine; + + private boolean processSingle = true; + + /** + * Instantiates a new http entry. + * + * @param version the version + * @param modelType the model type + * @param queryStyle the query style + * @param llBuilder the ll builder + */ + public HttpEntry(Version version, ModelType modelType, QueryStyle queryStyle, DBConnectionType connectionType) { + this.introspectorFactoryType = modelType; + this.queryStyle = queryStyle; + this.version = version; + this.loader = LoaderFactory.createLoaderForVersion(introspectorFactoryType, version); + this.dbEngine = new TitanDBEngine( + queryStyle, + connectionType, + loader); + //start transaction on creation + dbEngine.startTransaction(); + + } + + /** + * Gets the introspector factory type. + * + * @return the introspector factory type + */ + public ModelType getIntrospectorFactoryType() { + return introspectorFactoryType; + } + + /** + * Gets the query style. + * + * @return the query style + */ + public QueryStyle getQueryStyle() { + return queryStyle; + } + + /** + * Gets the version. + * + * @return the version + */ + public Version getVersion() { + return version; + } + + /** + * Gets the loader. + * + * @return the loader + */ + public Loader getLoader() { + return loader; + } + + /** + * Gets the db engine. + * + * @return the db engine + */ + public TransactionalGraphEngine getDbEngine() { + return dbEngine; + } + + public Pair<Boolean, List<Pair<URI, Response>>> process (List<DBRequest> requests, String sourceOfTruth) throws AAIException { + return this.process(requests, sourceOfTruth, true); + } + /** + * Process. + * @param requests the requests + * @param sourceOfTruth the source of truth + * + * @return the pair + * @throws AAIException the AAI exception + */ + public Pair<Boolean, List<Pair<URI, Response>>> process (List<DBRequest> requests, String sourceOfTruth, boolean enableResourceVersion) throws AAIException { + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + Response response = null; + Status status = Status.NOT_FOUND; + Introspector obj = null; + QueryParser query = null; + URI uri = null; + String transactionId = null; + UEBNotification notification = new UEBNotification(loader); + int depth = AAIProperties.MAXIMUM_DEPTH; + List<Pair<URI,Response>> responses = new ArrayList<>(); + MultivaluedMap<String, String> params = null; + HttpMethod method = null; + String uriTemp = ""; + Boolean success = true; + QueryEngine queryEngine = dbEngine.getQueryEngine(); + int maxRetries = 10; + int retry = 0; + for (DBRequest request : requests) { + try { + for (retry = 0; retry < maxRetries; ++retry) { + try { + method = request.getMethod(); + obj = request.getIntrospector(); + query = request.getParser(); + transactionId = request.getTransactionId(); + uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", ""); + uri = UriBuilder.fromPath(uriTemp).build(); + List<Vertex> vertices = query.getQueryBuilder().toList(); + boolean isNewVertex = false; + String outputMediaType = getMediaType(request.getHeaders().getAcceptableMediaTypes()); + String result = null; + params = request.getInfo().getQueryParameters(false); + depth = setDepth(obj, params.getFirst("depth")); + String cleanUp = params.getFirst("cleanup"); + String requestContext = ""; + List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context"); + if (requestContextList != null) { + requestContext = requestContextList.get(0); + } + + if (cleanUp == null) { + cleanUp = "false"; + } + if (vertices.size() > 1 && processSingle && !method.equals(HttpMethod.GET)) { + if (method.equals(HttpMethod.DELETE)) { + throw new AAIException("AAI_6138"); + } else { + throw new AAIException("AAI_6137"); + } + } + if (method.equals(HttpMethod.PUT)) { + String resourceVersion = (String)obj.getValue("resource-version"); + if (vertices.size() == 1) { + if (enableResourceVersion) { + serializer.verifyResourceVersion("update", query.getResultType(), (String)vertices.get(0).<String>property("resource-version").orElse(null), resourceVersion, obj.getURI()); + } + isNewVertex = false; + } else { + if (enableResourceVersion) { + serializer.verifyResourceVersion("create", query.getResultType(), "", resourceVersion, obj.getURI()); + } + isNewVertex = true; + } + } else { + if (vertices.size() == 0) { + String msg = createNotFoundMessage(query.getResultType(), request.getUri()); + throw new AAIException("AAI_6114", msg); + } else { + isNewVertex = false; + } + } + Vertex v = null; + if (!isNewVertex) { + v = vertices.get(0); + } + HashMap<String, Introspector> relatedObjects = new HashMap<>(); + switch (method) { + case GET: + String nodeOnly = params.getFirst("nodes-only"); + boolean isNodeOnly = nodeOnly != null; + + obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(), depth, isNodeOnly, cleanUp); + if (obj != null) { + status = Status.OK; + MarshallerProperties properties; + if (!request.getMarshallerProperties().isPresent()) { + properties = + new MarshallerProperties.Builder(org.openecomp.aai.restcore.MediaType.getEnum(outputMediaType)).build(); + } else { + properties = request.getMarshallerProperties().get(); + } + result = obj.marshal(properties); + } + + break; + case PUT: + response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); + if (isNewVertex) { + v = serializer.createNewVertex(obj); + } else { + serializer.touchStandardVertexProperties(v, false); + } + serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext); + this.invokeExtension(dbEngine, this.dbEngine.tx(), HttpMethod.PUT, request, sourceOfTruth, version, loader, obj, uri, false); + status = Status.OK; + if (isNewVertex) { + status = Status.CREATED; + } + obj = serializer.getLatestVersionView(v); + if (query.isDependent()) { + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + } + notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects); + + break; + case PUT_EDGE: + serializer.touchStandardVertexProperties(v, false); + serializer.createEdge(obj, v); + status = Status.OK; + break; + case MERGE_PATCH: + Introspector existingObj = (Introspector) obj.clone(); + existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp); + String existingJson = existingObj.marshal(false); + String newJson; + + if (request.getRawRequestContent().isPresent()) { + newJson = request.getRawRequestContent().get(); + } else { + newJson = ""; + } + Object relationshipList = request.getIntrospector().getValue("relationship-list"); + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode existingNode = mapper.readTree(existingJson); + JsonNode newNode = mapper.readTree(newJson); + JsonMergePatch patch = JsonMergePatch.fromJson(newNode); + JsonNode completed = patch.apply(existingNode); + String patched = mapper.writeValueAsString(completed); + Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched); + if (relationshipList == null) { + //if the caller didn't touch the relationship-list, we shouldn't either + patchedObj.setValue("relationship-list", null); + } + serializer.touchStandardVertexProperties(v, false); + serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext); + status = Status.OK; + patchedObj = serializer.getLatestVersionView(v); + if (query.isDependent()) { + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + } + notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, patchedObj, relatedObjects); + } catch (IOException | JsonPatchException e) { + throw new AAIException("AAI_3000", "could not perform patch operation"); + } + break; + case DELETE: + String resourceVersion = params.getFirst("resource-version"); + obj = serializer.getLatestVersionView(v); + if (query.isDependent()) { + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + } + this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); + serializer.delete(v, resourceVersion, enableResourceVersion); + this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, false); + status = Status.NO_CONTENT; + notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects); + break; + case DELETE_EDGE: + serializer.touchStandardVertexProperties(v, false); + serializer.deleteEdge(obj, v); + status = Status.NO_CONTENT; + break; + default: + break; + } + + + /* temporarily adding vertex id to the headers + * to be able to use for testing the vertex id endpoint functionality + * since we presently have no other way of generating those id urls + */ + if (response == null && v != null && ( + method.equals(HttpMethod.PUT) + || method.equals(HttpMethod.GET) + || method.equals(HttpMethod.MERGE_PATCH)) + ) { + String myvertid = v.id().toString(); + response = Response.status(status) + .header("vertex-id", myvertid) + .entity(result) + .type(outputMediaType).build(); + } else if (response == null) { + response = Response.status(status) + .type(outputMediaType).build(); + } else { + //response already set to something + } + Pair<URI,Response> pairedResp = Pair.with(request.getUri(), response); + responses.add(pairedResp); + //break out of retry loop + break; + } catch (TitanException e) { + this.dbEngine.rollback(); + AAIException ex = new AAIException("AAI_6142", e); + ErrorLogHelper.logException(ex); + Thread.sleep((retry + 1) * 20); + this.dbEngine.startTransaction(); + queryEngine = dbEngine.getQueryEngine(); + serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + } + + if (retry == maxRetries) { + throw new AAIException("AAI_6134"); + } + } + } catch (AAIException e) { + success = false; + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(request.getMethod().toString()); //GET, PUT, etc + templateVars.add(request.getUri().getPath().toString()); + templateVars.addAll(e.getTemplateVars()); + + response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars)) + .build(); + Pair<URI,Response> pairedResp = Pair.with(request.getUri(), response); + responses.add(pairedResp); + continue; + } catch (Exception e) { + success = false; + e.printStackTrace(); + AAIException ex = new AAIException("AAI_4000", e); + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(request.getMethod().toString()); //GET, PUT, etc + templateVars.add(request.getUri().getPath().toString()); + + response = Response + .status(ex.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars)) + .build(); + Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); + responses.add(pairedResp); + continue; + } + } + + notification.triggerEvents(); + Pair<Boolean, List<Pair<URI, Response>>> tuple = Pair.with(success, responses); + return tuple; + } + + /** + * Gets the media type. + * + * @param mediaTypeList the media type list + * @return the media type + */ + private String getMediaType(List <MediaType> mediaTypeList) { + String mediaType = MediaType.APPLICATION_JSON; // json is the default + for (MediaType mt : mediaTypeList) { + if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { + mediaType = MediaType.APPLICATION_XML; + } + } + return mediaType; + } + + /** + * Gets the object from db. + * + * @param serializer the serializer + * @param g the g + * @param query the query + * @param obj the obj + * @param uri the uri + * @param depth the depth + * @param cleanUp the clean up + * @return the object from db + * @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 + */ + private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { + + //nothing found + if (results.size() == 0) { + String msg = createNotFoundMessage(query.getResultType(), uri); + throw new AAIException("AAI_6114", msg); + } + + obj = serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); + + return obj; + } + + /** + * Invoke extension. + * + * @param dbEngine the db engine + * @param g the g + * @param httpMethod the http method + * @param transId the trans id + * @param fromAppId the from app id + * @param apiVersion the api version + * @param loader the loader + * @param obj the obj + * @param uri the uri + * @param headers the headers + * @param isPreprocess the is preprocess + * @return the response + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + private Response invokeExtension(TransactionalGraphEngine dbEngine, TitanTransaction g, HttpMethod httpMethod, DBRequest request, String fromAppId, Version apiVersion, Loader loader, Introspector obj, URI uri, boolean isPreprocess) throws IllegalArgumentException, UnsupportedEncodingException, AAIException { + AAIExtensionMap aaiExtMap = new AAIExtensionMap(); + ModelInjestor injestor = ModelInjestor.getInstance(); + Response response = null; + URIToExtensionInformation extensionInformation = new URIToExtensionInformation(loader, uri); + aaiExtMap.setHttpEntry(this); + aaiExtMap.setDbRequest(request); + aaiExtMap.setTransId(request.getTransactionId()); + aaiExtMap.setFromAppId(fromAppId); + aaiExtMap.setGraph(g); + aaiExtMap.setApiVersion(apiVersion.toString()); + aaiExtMap.setObjectFromRequest(obj); + aaiExtMap.setObjectFromRequestType(obj.getJavaClassName()); + aaiExtMap.setObjectFromResponse(obj); + aaiExtMap.setObjectFromResponseType(obj.getJavaClassName()); + aaiExtMap.setJaxbContext(injestor.getContextForVersion(apiVersion)); + aaiExtMap.setUri(uri.getRawPath()); + aaiExtMap.setTransactionalGraphEngine(dbEngine); + aaiExtMap.setLoader(loader); + aaiExtMap.setNamespace(extensionInformation.getNamespace()); + + ExtensionController ext = new ExtensionController(); + ext.runExtension(aaiExtMap.getApiVersion(), + extensionInformation.getNamespace(), + extensionInformation.getTopObject(), + extensionInformation.getMethodName(httpMethod, isPreprocess), + aaiExtMap, + isPreprocess); + + if (aaiExtMap.getPrecheckAddedList().size() > 0) { + response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders()); + } + + return response; + } + + /** + * Notify on skeleton creation. + * + * @param aaiExtMap the aai ext map + * @param input the input + * @param headers the headers + * @return the response + */ + //Legacy support + private Response notifyOnSkeletonCreation(AAIExtensionMap aaiExtMap, Introspector input, HttpHeaders headers) { + Response response = null; + HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>(); + + String keyString = ""; + + Set<String> resourceKeys = input.getKeys(); + for (String key : resourceKeys) { + keyString += key + "=" + input.getValue(key) + " "; + } + + for (AAIResponseMessage msg : aaiExtMap.getPrecheckResponseMessages().getAAIResponseMessage()) { + ArrayList<String> templateVars = new ArrayList<String>(); + + templateVars.add("PUT " + input.getDbName()); + templateVars.add(keyString); + List<String> keys = new ArrayList<String>(); + templateVars.add(msg.getAaiResponseMessageResourceType()); + for (AAIResponseMessageDatum dat : msg.getAaiResponseMessageData().getAAIResponseMessageDatum()) { + keys.add(dat.getAaiResponseMessageDatumKey() + "=" + dat.getAaiResponseMessageDatumValue()); + } + templateVars.add(StringUtils.join(keys, ", ")); + exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()), + templateVars); + } + response = Response + .status(Status.ACCEPTED).entity(ErrorLogHelper + .getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList)) + .build(); + + return response; + } + + /** + * Creates the not found message. + * + * @param resultType the result type + * @param uri the uri + * @return the string + */ + private String createNotFoundMessage(String resultType, URI uri) { + + String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); + + return msg; + } + + /** + * Sets the depth. + * + * @param depthParam the depth param + * @return the int + * @throws AAIException the AAI exception + */ + protected int setDepth(Introspector obj, String depthParam) throws AAIException { + int depth = AAIProperties.MAXIMUM_DEPTH; + + if(depthParam == null){ + if(this.version.compareTo(Version.v9) >= 0){ + depth = 0; + } else { + depth = AAIProperties.MAXIMUM_DEPTH; + } + } else { + if (depthParam.length() > 0 && !depthParam.equals("all")){ + try { + depth = Integer.valueOf(depthParam); + } catch (Exception e) { + throw new AAIException("AAI_4016"); + } + + } + } + String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); + + int maximumDepth = AAIProperties.MAXIMUM_DEPTH; + + if(maxDepth != null){ + try { + maximumDepth = Integer.parseInt(maxDepth); + } catch(Exception ex){ + throw new AAIException("AAI_4018"); + } + } + + if(depth > maximumDepth){ + throw new AAIException("AAI_3303"); + } + + return depth; + } + + /** + * Checks if is modification method. + * + * @param method the method + * @return true, if is modification method + */ + private boolean isModificationMethod(HttpMethod method) { + boolean result = false; + + if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) || method.equals(HttpMethod.MERGE_PATCH)) { + result = true; + } + + return result; + + } + + private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + HashMap<String, Introspector> relatedVertices = new HashMap<>(); + List<Vertex> vertexChain = queryEngine.findParents(v); + for (Vertex vertex : vertexChain) { + try { + final Introspector vertexObj = serializer.getVertexProperties(vertex); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); + } + + } + + return relatedVertices; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/RetiredConsumer.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/RetiredConsumer.java new file mode 100644 index 0000000..99974ef --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/RetiredConsumer.java @@ -0,0 +1,143 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.retired; + +import java.util.ArrayList; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.cxf.jaxrs.ext.PATCH; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.restcore.RESTAPI; +import org.openecomp.aai.util.AAIConfig; + +/** + * The Class RetiredConsumer. + */ +public abstract class RetiredConsumer extends RESTAPI { + + /** + * Creates the message get. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @GET + @Path("/{uri:.*}") + public Response createMessageGet(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + /** + * Creates the message delete. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @DELETE + @Path("/{uri:.*}") + public Response createMessageDelete(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + /** + * Creates the message post. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @POST + @Path("/{uri:.*}") + public Response createMessagePost(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + @PATCH + @Path("/{uri:.*}") + public Response createMessagePatch(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + /** + * Creates the message put. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PUT + @Path("/{uri:.*}") + public Response createMessagePut(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + + /** + * Creates the message. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + private Response createMessage(String versionParam, HttpHeaders headers, UriInfo info, HttpServletRequest req) { + AAIException e = new AAIException("AAI_3007"); + + ArrayList<String> templateVars = new ArrayList<String>(); + + if (templateVars.size() == 0) { + templateVars.add("PUT"); + templateVars.add(info.getPath().toString()); + templateVars.add(versionParam); + templateVars.add(AAIConfig.get("aai.default.api.version", "")); + } + + Response response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, + templateVars)).build(); + + return response; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V3ThroughV7Consumer.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V3ThroughV7Consumer.java new file mode 100644 index 0000000..55d9482 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V3ThroughV7Consumer.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.retired; + +import javax.ws.rs.Path; + +@Path("{version: v[3-7]}") +public class V3ThroughV7Consumer extends RetiredConsumer { + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V7V8NamedQueries.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V7V8NamedQueries.java new file mode 100644 index 0000000..3f022a2 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/retired/V7V8NamedQueries.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.retired; + +import javax.ws.rs.Path; + +@Path("{version: v[78]}/service-design-and-creation/named-queries") +public class V7V8NamedQueries extends RetiredConsumer { + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GenericQueryProcessor.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GenericQueryProcessor.java new file mode 100644 index 0000000..2e9e6a5 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GenericQueryProcessor.java @@ -0,0 +1,227 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.io.FileNotFoundException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + +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.__; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.javatuples.Pair; + +import org.openecomp.aai.restcore.util.URITools; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.queryformats.SubGraphStyle; + +import jersey.repackaged.com.google.common.base.Joiner; + +public abstract class GenericQueryProcessor { + + protected final Optional<URI> uri; + protected final MultivaluedMap<String, String> queryParams; + protected final Optional<Collection<Vertex>> vertices; + protected static Pattern p = Pattern.compile("query/(.*+)"); + protected Optional<String> gremlin; + protected final TransactionalGraphEngine dbEngine; + protected static GremlinServerSingleton gremlinServerSingleton = GremlinServerSingleton.getInstance(); + protected final boolean isGremlin; + + protected GenericQueryProcessor(Builder builder) { + this.uri = builder.getUri(); + this.dbEngine = builder.getDbEngine(); + this.vertices = builder.getVertices(); + this.gremlin = builder.getGremlin(); + this.isGremlin = builder.isGremlin(); + if (uri.isPresent()) { + queryParams = URITools.getQueryMap(uri.get()); + } else { + queryParams = new MultivaluedHashMap<>(); + } + } + + protected abstract GraphTraversal<?,?> runQuery(String query, Map<String, Object> params); + + protected List<Object> processSubGraph(SubGraphStyle style, GraphTraversal<?,?> g) { + final List<Object> resultVertices = new Vector<>(); + g.store("x"); + + if (SubGraphStyle.prune.equals(style) || SubGraphStyle.star.equals(style)) { + g.barrier().bothE(); + if (SubGraphStyle.prune.equals(style)) { + g.where(__.otherV().where(P.within("x"))); + } + g.dedup().subgraph("subGraph").cap("subGraph").map(x -> (Graph)x.get()).next().traversal().V().forEachRemaining(x -> { + resultVertices.add(x); + }); + } else { + resultVertices.addAll(g.toList()); + } + return resultVertices; + } + + public List<Object> execute(SubGraphStyle style) throws FileNotFoundException { + final List<Object> resultVertices; + + Pair<String, Map<String, Object>> tuple = this.createQuery(); + String query = tuple.getValue0(); + Map<String, Object> params = tuple.getValue1(); + + if (query.equals("") && (vertices.isPresent() && vertices.get().isEmpty())) { + //nothing to do, just exit + return new ArrayList<>(); + } + GraphTraversal<?,?> g = this.runQuery(query, params); + + resultVertices = this.processSubGraph(style, g); + + return resultVertices; + } + + protected Pair<String, Map<String, Object>> createQuery() { + Map<String, Object> params = new HashMap<>(); + String query = ""; + if (!this.isGremlin) { + Matcher m = p.matcher(uri.get().getPath()); + String queryName = ""; + if (m.find()) { + queryName = m.group(1); + } + + for (String key : queryParams.keySet()) { + params.put(key, queryParams.getFirst(key)); + } + + query = gremlinServerSingleton.getStoredQuery(queryName); + if (query == null) { + query = ""; + } + + List<Object> ids = new ArrayList<>(); + + if (vertices.isPresent() && !vertices.get().isEmpty()) { + for (Vertex v : vertices.get()) { + ids.add(v.id()); + } + StringBuilder sb = new StringBuilder(); + sb.append("["); + sb.append(Joiner.on(",").join(ids)); + sb.append("]"); + String startPrefix = "aaiStartQuery = " + sb.toString() + " as Object[];g.V(aaiStartQuery)"; + if (!"".equals(query)) { + query = startPrefix + "." + query; + } else { + query = startPrefix; + } + } + + } else { + query = gremlin.get(); + } + + return new Pair<>(query, params); + } + + public static class Builder { + + private final TransactionalGraphEngine dbEngine; + private Optional<URI> uri = Optional.empty(); + private Optional<String> gremlin = Optional.empty(); + private boolean isGremlin = false; + private Optional<Collection<Vertex>> vertices = Optional.empty(); + private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER; + + public Builder(TransactionalGraphEngine dbEngine) { + this.dbEngine = dbEngine; + } + + public Builder queryFrom(URI uri) { + this.uri = Optional.of(uri); + this.isGremlin = false; + return this; + } + + public Builder startFrom(Collection<Vertex> vertices) { + this.vertices = Optional.of(vertices); + return this; + } + + public Builder queryFrom(String gremlin) { + this.gremlin = Optional.of(gremlin); + this.isGremlin = true; + return this; + } + + public Builder processWith(QueryProcessorType type) { + this.processorType = type; + return this; + } + public TransactionalGraphEngine getDbEngine() { + return dbEngine; + } + + public Optional<URI> getUri() { + return uri; + } + + public Optional<String> getGremlin() { + return gremlin; + } + + public boolean isGremlin() { + return isGremlin; + } + + public Optional<Collection<Vertex>> getVertices() { + return vertices; + } + + public QueryProcessorType getProcessorType() { + return processorType; + } + + public GenericQueryProcessor create() { + + if (this.getProcessorType().equals(QueryProcessorType.GREMLIN_SERVER)) { + return new GremlinServerImpl(this); + } else if (this.getProcessorType().equals(QueryProcessorType.LOCAL_GROOVY)) { + return new GroovyShellImpl(this); + } else { + return new GremlinServerImpl(this); + } + } + + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinGroovyShellSingleton.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinGroovyShellSingleton.java new file mode 100644 index 0000000..64159a4 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinGroovyShellSingleton.java @@ -0,0 +1,88 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.ImportCustomizer; + +import groovy.lang.Binding; +import groovy.lang.GroovyShell; +import groovy.lang.Script; +import groovy.transform.TimedInterrupt; + +/** + * Creates and returns a groovy shell with the + * configuration to statically import graph classes + * + */ +public class GremlinGroovyShellSingleton { + + private final GroovyShell shell; + private GremlinGroovyShellSingleton() { + Map<String, Object> parameters = new HashMap<>(); + parameters.put("value", 30000); + parameters.put("unit", new PropertyExpression(new ClassExpression(ClassHelper.make(TimeUnit.class)),"MILLISECONDS")); + + ASTTransformationCustomizer custom = new ASTTransformationCustomizer(parameters, TimedInterrupt.class); + ImportCustomizer imports = new ImportCustomizer(); + imports.addStaticStars( + "org.apache.tinkerpop.gremlin.process.traversal.P" + ); + imports.addImports( + "org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__", + "org.apache.tinkerpop.gremlin.structure.T", + "org.apache.tinkerpop.gremlin.process.traversal.P"); + CompilerConfiguration config = new CompilerConfiguration(); + config.addCompilationCustomizers(custom, imports); + + this.shell = new GroovyShell(config); + } + + private static class Helper { + private static final GremlinGroovyShellSingleton INSTANCE = new GremlinGroovyShellSingleton(); + } + + public static GremlinGroovyShellSingleton getInstance() { + + return Helper.INSTANCE; + } + + /** + * @param traversal + * @param params + * @return result of graph traversal + */ + public GraphTraversal<?, ?> executeTraversal (String traversal, Map<String, Object> params) { + Binding binding = new Binding(params); + Script script = shell.parse(traversal); + script.setBinding(binding); + return (GraphTraversal<?, ?>) script.run(); + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerImpl.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerImpl.java new file mode 100644 index 0000000..a759b86 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerImpl.java @@ -0,0 +1,76 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.apache.tinkerpop.gremlin.driver.ResultSet; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; + +import org.openecomp.aai.util.AAIConfig; + +public class GremlinServerImpl extends GenericQueryProcessor { + + + protected GremlinServerImpl(Builder builder) { + super(builder); + } + + + @Override + protected GraphTraversal<?,?> runQuery(String query, Map<String, Object> params) { + + //must force them into ids because of serialization issue with + //tinkerpop-3.0.1-incubating + query += ".id()"; + String rebindGraph = AAIConfig.get("aai.server.rebind", "g"); + + if(!"g".equals(rebindGraph)){ + query = query.replaceFirst("g\\.V\\(", rebindGraph + ".V("); + } + + Cluster cluster = gremlinServerSingleton.getCluster(); + Client client = cluster.connect(); + + ResultSet results = client.submit(query, params); + + + List<Object> vIds = new Vector<>(); + results.stream().forEach(x -> { + Object obj = x.getObject(); + vIds.add(obj); + }); + + client.close(); + + if (vIds.isEmpty()) { + return __.start(); + } else { + return this.dbEngine.asAdmin().getTraversalSource().V(vIds.toArray()); + } + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerSingleton.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerSingleton.java new file mode 100644 index 0000000..c54b310 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GremlinServerSingleton.java @@ -0,0 +1,130 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import org.openecomp.aai.util.AAIConstants; +import org.openecomp.aai.util.FileWatcher; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.apache.tinkerpop.gremlin.driver.Cluster; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Date; +import java.util.Properties; +import java.util.Timer; +import java.util.TimerTask; + +public class GremlinServerSingleton { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(GremlinServerSingleton.class); + + private Cluster cluster; + private boolean timerSet; + private Timer timer; + private Properties properties; + + private static class Helper { + private static final GremlinServerSingleton INSTANCE = new GremlinServerSingleton(); + } + + public static GremlinServerSingleton getInstance() { + return Helper.INSTANCE; + } + + private GremlinServerSingleton(){ + init(); + } + + /** + * Initializes the gremlin server singleton + * Loads the configuration of the gremlin server and creates a cluster + * Loads the gremlin query file into the properties object + * Then creates a file watcher to watch the file every ten seconds + * and if there is a change in the file, then reloads the file into + * the properties object + * + */ + private void init() { + + properties = new Properties(); + + try { + cluster = Cluster.build(new File(AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "gremlin-server-config.yaml")) + .maxContentLength(6537920) + .create(); + } catch (FileNotFoundException e) { + logger.error("Unable to find the file: " + e); + } + + File queryFile = new File(AAIConstants.AAI_HOME_ETC_QUERY); + + try (FileInputStream fis = new FileInputStream(queryFile)){ + properties.load(fis); + } catch (IOException e) { + logger.error("Error occurred during the processing of query file: " + e); + } + + + TimerTask task = new FileWatcher(new File(AAIConstants.AAI_HOME_ETC_QUERY)) { + @Override + protected void onChange(File file) { + File queryFile = new File(AAIConstants.AAI_HOME_ETC_QUERY); + try (FileInputStream fis = new FileInputStream(queryFile)){ + properties.load(fis); + logger.debug("File: " + file + " was changed so the cluster is rebuild for gremlin server"); + } catch (FileNotFoundException e) { + logger.error("Unable to find the file: " + e); + } catch (IOException e) { + logger.error("Error occurred during the processing of query file: " + e); + } + } + }; + + if (!timerSet) { + timerSet = true; + timer = new Timer(); + timer.schedule( task , new Date(), 10000 ); + } + + } + + public Cluster getCluster(){ + return cluster; + } + + /** + * Gets the key if the properties contains that key + * + * Purposely not checking if the property exists due + * to if you check for the property and then get the property + * Then you are going to have to synchronize the method + * + * @param key the query to check if it exists in the file + * @return string if the key exists or null if it doesn't + */ + public String getStoredQuery(String key){ + return (String) properties.get(key); + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GroovyShellImpl.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GroovyShellImpl.java new file mode 100644 index 0000000..1b64d21 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/GroovyShellImpl.java @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.util.Map; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +import org.openecomp.aai.restcore.search.GremlinGroovyShellSingleton; + +public class GroovyShellImpl extends GenericQueryProcessor { + + protected GroovyShellImpl(Builder builder) { + super(builder); + } + + @Override + protected GraphTraversal<?,?> runQuery(String query, Map<String, Object> params) { + + params.put("g", this.dbEngine.asAdmin().getTraversalSource()); + + GremlinGroovyShellSingleton shell = GremlinGroovyShellSingleton.getInstance(); + + return shell.executeTraversal(query, params); + } + +} + + diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/ModelAndNamedQueryRestProvider.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/ModelAndNamedQueryRestProvider.java new file mode 100644 index 0000000..f333dd8 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/ModelAndNamedQueryRestProvider.java @@ -0,0 +1,203 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.util.ArrayList; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.openecomp.aai.dbgraphmap.SearchGraph; +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.restcore.RESTAPI; +import org.openecomp.aai.util.AAIApiVersion; + +/** + * Implements the search subdomain in the REST API. All API calls must include + * X-FromAppId and X-TransactionId in the header. + * + + * + */ + +@Path("/search") +public class ModelAndNamedQueryRestProvider extends RESTAPI { + + protected static String authPolicyFunctionName = "search"; + + public static final String NAMED_QUERY = "/named-query"; + + public static final String MODEL_QUERY = "/model"; + + /** + * Gets the named query response. + * + * @param headers the headers + * @param req the req + * @param queryParameters the query parameters + * @return the named query response + */ + /* ---------------- Start Named Query --------------------- */ + @POST + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Path(NAMED_QUERY) + public Response getNamedQueryResponse(@Context HttpHeaders headers, + @Context HttpServletRequest req, + String queryParameters) { + AAIException ex = null; + Response response = null; + String fromAppId = null; + String transId = null; + String rqstTm = genDate(); + ArrayList<String> templateVars = new ArrayList<String>(); + try { + fromAppId = getFromAppId(headers); + transId = getTransId(headers); + + AAIExtensionMap aaiExtMap = new AAIExtensionMap(); + aaiExtMap.setHttpHeaders(headers); + aaiExtMap.setServletRequest(req); + aaiExtMap.setApiVersion(AAIApiVersion.get()); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + //only consider header value for search + DBConnectionType type = this.determineConnectionType("force-cache", realTime); + + SearchGraph searchGraph = new SearchGraph(); + response = searchGraph.runNamedQuery(fromAppId, transId, queryParameters, type, aaiExtMap); + + String respTm = genDate(); + logTransaction(fromAppId, transId, "GETSDNZONERESPONSE", + req.getRequestURI(), rqstTm, respTm, "", response); + + } catch (AAIException e) { + // send error response + ex = e; + templateVars.add("POST Search"); + templateVars.add("getNamedQueryResponse"); + response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } catch (Exception e) { + // send error response + ex = new AAIException("AAI_4000", e); + templateVars.add("POST Search"); + templateVars.add("getNamedQueryResponse"); + response = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } finally { + // log success or failure + if (ex != null) { + ErrorLogHelper.logException(ex); + } + } + return response; + } + + /** + * Gets the model query response. + * + * @param headers the headers + * @param req the req + * @param inboundPayload the inbound payload + * @param action the action + * @return the model query response + */ + /* ---------------- Start Named Query --------------------- */ + @POST + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Path(MODEL_QUERY) + public Response getModelQueryResponse(@Context HttpHeaders headers, + @Context HttpServletRequest req, + String inboundPayload, + @QueryParam("action") String action) { + AAIException ex = null; + Response response = null; + String fromAppId = null; + String transId = null; + String rqstTm = genDate(); + ArrayList<String> templateVars = new ArrayList<String>(); + try { + fromAppId = getFromAppId(headers); + transId = getTransId(headers); + + AAIExtensionMap aaiExtMap = new AAIExtensionMap(); + aaiExtMap.setHttpHeaders(headers); + aaiExtMap.setServletRequest(req); + aaiExtMap.setApiVersion(AAIApiVersion.get()); + aaiExtMap.setFromAppId(fromAppId); + aaiExtMap.setTransId(transId); + + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + //only consider header value for search + DBConnectionType type = this.determineConnectionType("force-cache", realTime); + + SearchGraph searchGraph = new SearchGraph(); + if (action != null && action.equalsIgnoreCase("DELETE")) { + response = searchGraph.executeModelOperation(fromAppId, transId, inboundPayload, type, true, aaiExtMap); + } else { + response = searchGraph.executeModelOperation(fromAppId, transId, inboundPayload, type, false, aaiExtMap); + } + String respTm = genDate(); + logTransaction(fromAppId, transId, "POSTMODELQUERYRESPONSE", + req.getRequestURI(), rqstTm, respTm, "", response); + + } catch (AAIException e) { + // send error response + ex = e; + templateVars.add("POST Search"); + templateVars.add("getModelQueryResponse"); + response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } catch (Exception e) { + // send error response + ex = new AAIException("AAI_4000", e); + templateVars.add("POST Search"); + templateVars.add("getModelQueryResponse"); + response = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } finally { + // log success or failure + if (ex != null) { + ErrorLogHelper.logException(ex); + } + } + return response; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/QueryProcessorType.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/QueryProcessorType.java new file mode 100644 index 0000000..33d7a90 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/QueryProcessorType.java @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +public enum QueryProcessorType { + + GREMLIN_SERVER, + LOCAL_GROOVY +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/search/SearchProvider.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/SearchProvider.java new file mode 100644 index 0000000..d6712ce --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/search/SearchProvider.java @@ -0,0 +1,269 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.search; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.dbgraphmap.SearchGraph; +import org.openecomp.aai.dbmap.DBConnectionType; +import org.openecomp.aai.domain.model.AAIResource; +import org.openecomp.aai.domain.model.AAIResources; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.ingestModel.IngestModelMoxyOxm; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.LoaderFactory; +import org.openecomp.aai.introspection.ModelType; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.restcore.RESTAPI; +import org.openecomp.aai.serialization.db.DBSerializer; +import org.openecomp.aai.serialization.engines.QueryStyle; +import org.openecomp.aai.serialization.engines.TitanDBEngine; +import org.openecomp.aai.serialization.engines.TransactionalGraphEngine; +import org.openecomp.aai.serialization.queryformats.utils.UrlBuilder; +import org.openecomp.aai.util.AAIApiVersion; + +/** + * Implements the search subdomain in the REST API. All API calls must include + * X-FromAppId and X-TransactionId in the header. + * + + * + */ + +@Path("/{version: v2|v[789]|v1[0]|latest}/search") +public class SearchProvider extends RESTAPI { + + protected static String authPolicyFunctionName = "search"; + + public static final String GENERIC_QUERY = "/generic-query"; + + public static final String NODES_QUERY = "/nodes-query"; + + /** + * Gets the generic query response. + * + * @param headers the headers + * @param req the req + * @param startNodeType the start node type + * @param startNodeKeyParams the start node key params + * @param includeNodeTypes the include node types + * @param depth the depth + * @return the generic query response + */ + /* ---------------- Start Generic Query --------------------- */ + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Path(GENERIC_QUERY) + public Response getGenericQueryResponse(@Context HttpHeaders headers, + @Context HttpServletRequest req, + @QueryParam("start-node-type") final String startNodeType, + @QueryParam("key") final List<String> startNodeKeyParams, + @QueryParam("include") final List<String> includeNodeTypes, + @QueryParam("depth") final int depth, + @PathParam("version")String versionParam + ) { + + AAIException ex = null; + Response searchResult = null; + String fromAppId = null; + String transId = null; + String rqstTm = genDate(); + ArrayList<String> templateVars = new ArrayList<String>(); + try { + fromAppId = getFromAppId(headers); + transId = getTransId(headers); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + //only consider header value for search + DBConnectionType type = this.determineConnectionType("force-cache", realTime); + final Version version; + if (versionParam.equals("latest")) { + version = AAIProperties.LATEST; + } else { + version = Version.valueOf(versionParam); + } + final ModelType factoryType = ModelType.MOXY; + Loader loader = LoaderFactory.createLoaderForVersion(factoryType, version); + TransactionalGraphEngine dbEngine = new TitanDBEngine( + QueryStyle.TRAVERSAL, + type, + loader); + DBSerializer dbSerializer = new DBSerializer(version, dbEngine, factoryType, fromAppId); + UrlBuilder urlBuilder = new UrlBuilder(version, dbSerializer); + SearchGraph searchGraph = new SearchGraph(); + searchResult = searchGraph.runGenericQuery( + headers, + startNodeType, + startNodeKeyParams, + includeNodeTypes, + depth, + dbEngine, + loader, + urlBuilder + + ); + + String respTm = genDate(); + logTransaction(fromAppId, transId, + "GETGENERICQUERYRESPONSE", req.getRequestURI(), rqstTm, respTm, + "", searchResult); + + } catch (AAIException e) { + // send error response + ex = e; + templateVars.add("GET Search"); + templateVars.add("getGenericQueryResponse"); + searchResult = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } catch (Exception e) { + // send error response + ex = new AAIException("AAI_4000", e); + templateVars.add("GET Search"); + templateVars.add("getGenericQueryResponse"); + searchResult = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } finally { + // log success or failure + if (ex != null){ + ErrorLogHelper.logException(ex); + } + } + + return searchResult; + } + + /* ---------------- End Generic Query --------------------- */ + + /** + * Gets the nodes query response. + * + * @param headers the headers + * @param req the req + * @param searchNodeType the search node type + * @param edgeFilterList the edge filter list + * @param filterList the filter list + * @return the nodes query response + */ + /* ---------------- Start Nodes Query --------------------- */ + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Path(NODES_QUERY) + public Response getNodesQueryResponse(@Context HttpHeaders headers, + @Context HttpServletRequest req, + @QueryParam("search-node-type") final String searchNodeType, + @QueryParam("edge-filter") final List<String> edgeFilterList, + @QueryParam("filter") final List<String> filterList, + @PathParam("version")String versionParam) { + AAIException ex = null; + Response searchResult = null; + String fromAppId = null; + String transId = null; + String rqstTm = genDate(); + ArrayList<String> templateVars = new ArrayList<String>(); + try { + fromAppId = getFromAppId(headers); + transId = getTransId(headers); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + //only consider header value for search + DBConnectionType type = this.determineConnectionType("force-cache", realTime); + + final Version version; + if (versionParam.equals("latest")) { + version = AAIProperties.LATEST; + } else { + version = Version.valueOf(versionParam); + } + final ModelType factoryType = ModelType.MOXY; + Loader loader = LoaderFactory.createLoaderForVersion(factoryType, version); + TransactionalGraphEngine dbEngine = new TitanDBEngine( + QueryStyle.TRAVERSAL, + type, + loader); + DBSerializer dbSerializer = new DBSerializer(version, dbEngine, factoryType, fromAppId); + UrlBuilder urlBuilder = new UrlBuilder(version, dbSerializer); + SearchGraph searchGraph = new SearchGraph(); + + searchResult = searchGraph.runNodesQuery(headers, + searchNodeType, + edgeFilterList, + filterList, + dbEngine, + loader, + urlBuilder); + + String respTm = genDate(); + logTransaction(fromAppId, transId, "GETNODESQUERYRESPONSE", + req.getRequestURI(), rqstTm, respTm, "", searchResult); + } catch (AAIException e) { + // send error response + ex = e; + templateVars.add("GET Search"); + templateVars.add("getNodesQueryResponse"); + searchResult = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } catch (Exception e) { + // send error response + ex = new AAIException("AAI_4000", e); + templateVars.add("GET Search"); + templateVars.add("getNodesQueryResponse"); + searchResult = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } finally { + // log success or failure + if (ex != null){ + ErrorLogHelper.logException(ex); + } + } + return searchResult; + } + + + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/NotificationEvent.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/NotificationEvent.java new file mode 100644 index 0000000..389d296 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/NotificationEvent.java @@ -0,0 +1,96 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.ueb; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.util.StoreNotificationEvent; + +/** + * The Class NotificationEvent. + */ +public class NotificationEvent { + + private Loader loader = null; + + private Introspector eventHeader = null; + + private Introspector obj = null; + + /** + * Instantiates a new notification event. + * + * @param version the version + * @param eventHeader the event header + * @param obj the obj + */ + public NotificationEvent (Loader loader, Introspector eventHeader, Introspector obj) { + this.loader = loader; + this.eventHeader = eventHeader; + this.obj = obj; + } + + /** + * Trigger. + * + * @throws AAIException the AAI exception + */ + public void trigger() throws AAIException { + + StoreNotificationEvent sne = new StoreNotificationEvent(); + + sne.storeEvent(loader, eventHeader, obj); + + } + + /** + * Gets the notification version. + * + * @return the notification version + */ + public Version getNotificationVersion() { + return loader.getVersion(); + } + + /** + * Gets the event header. + * + * @return the event header + */ + public Introspector getEventHeader() { + return eventHeader; + } + + /** + * Gets the obj. + * + * @return the obj + */ + public Introspector getObj() { + return obj; + } + + + + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/UEBNotification.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/UEBNotification.java new file mode 100644 index 0000000..397082f --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/ueb/UEBNotification.java @@ -0,0 +1,178 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.ueb; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.ws.rs.core.Response.Status; + +import org.openecomp.aai.db.props.AAIProperties; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.LoaderFactory; +import org.openecomp.aai.introspection.Version; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; +import org.openecomp.aai.introspection.exceptions.AAIUnmarshallingException; +import org.openecomp.aai.parsers.uri.URIToObject; +import org.openecomp.aai.util.AAIConfig; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +/** + * The Class UEBNotification. + */ +public class UEBNotification { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(UEBNotification.class); + + private Loader currentVersionLoader = null; + protected List<NotificationEvent> events = null; + private String urlBase = null; + private Version notificationVersion = null; + + /** + * Instantiates a new UEB notification. + * + * @param loader the loader + */ + public UEBNotification(Loader loader) { + events = new ArrayList<>(); + currentVersionLoader = LoaderFactory.createLoaderForVersion(loader.getModelType(), AAIProperties.LATEST); + urlBase = AAIConfig.get("aai.server.url.base",""); + notificationVersion = Version.valueOf(AAIConfig.get("aai.notification.current.version","v10")); + } + + + /** + * Creates the notification event. + * + * @param transactionId the X-TransactionId + * @param sourceOfTruth + * @param status the status + * @param uri the uri + * @param obj the obj + * @throws AAIException the AAI exception + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public void createNotificationEvent(String transactionId, String sourceOfTruth, Status status, URI uri, Introspector obj, HashMap<String, Introspector> relatedObjects) throws AAIException, IllegalArgumentException, UnsupportedEncodingException { + + String action = "UPDATE"; + + if (status.equals(Status.CREATED)) { + action = "CREATE"; + } else if (status.equals(Status.OK)) { + action = "UPDATE"; + } else if (status.equals(Status.NO_CONTENT)) { + action = "DELETE"; + } + + try { + Introspector eventHeader = currentVersionLoader.introspectorFromName("notification-event-header"); + URIToObject parser = new URIToObject(currentVersionLoader, uri, relatedObjects); + + String entityLink = ""; + if (uri.toString().startsWith("/")) { + entityLink = urlBase + notificationVersion + uri; + } else { + entityLink = urlBase + notificationVersion + "/" + uri; + } + + + eventHeader.setValue("entity-link", entityLink); + eventHeader.setValue("action", action); + eventHeader.setValue("entity-type", obj.getDbName()); + eventHeader.setValue("top-entity-type", parser.getTopEntityName()); + eventHeader.setValue("source-name", sourceOfTruth); + eventHeader.setValue("version", notificationVersion.toString()); + eventHeader.setValue("id", transactionId); + + List<Object> parentList = parser.getParentList(); + parentList.clear(); + + if (!parser.getTopEntity().equals(parser.getEntity())) { + Introspector child = obj; + if (!parser.getLoader().getVersion().equals(obj.getVersion())) { + String json = obj.marshal(false); + child = parser.getLoader().unmarshal(parser.getEntity().getName(), json); + } + + //wrap the child object in its parents + parentList.add(child.getUnderlyingObject()); + } + + final Introspector eventObject; + + //convert to most resent version + if (!parser.getLoader().getVersion().equals(currentVersionLoader.getVersion())) { + String json = ""; + if (parser.getTopEntity().equals(parser.getEntity())) { + //convert the parent object passed in + json = obj.marshal(false); + eventObject = currentVersionLoader.unmarshal(obj.getName(), json); + } else { + //convert the object created in the parser + json = parser.getTopEntity().marshal(false); + eventObject = currentVersionLoader.unmarshal(parser.getTopEntity().getName(), json); + } + } else { + if (parser.getTopEntity().equals(parser.getEntity())) { + //take the top level parent object passed in + eventObject = obj; + } else { + //take the wrapped child objects (ogres are like onions) + eventObject = parser.getTopEntity(); + } + } + + final NotificationEvent event = new NotificationEvent(currentVersionLoader, eventHeader, eventObject); + events.add(event); + } catch (AAIUnknownObjectException e) { + throw new RuntimeException("Fatal error - notification-event-header object not found!"); + } catch (AAIUnmarshallingException e) { + LOGGER.error("Unmarshalling error occurred while generating UEBNotification", e); + } + } + + /** + * Trigger events. + * + * @throws AAIException the AAI exception + */ + public void triggerEvents() throws AAIException { + for (NotificationEvent event : events) { + event.trigger(); + } + events.clear(); + } + + public List<NotificationEvent> getEvents() { + return this.events; + } + + + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/util/EchoResponse.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/EchoResponse.java new file mode 100644 index 0000000..6d01435 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/EchoResponse.java @@ -0,0 +1,121 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.util; + +import java.util.ArrayList; +import java.util.HashMap; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.logging.ErrorLogHelper; +import org.openecomp.aai.restcore.RESTAPI; + +/** + * The Class EchoResponse. + */ +public class EchoResponse extends RESTAPI { + + protected static String authPolicyFunctionName = "util"; + + public static final String echoPath = "/util/echo"; + + /** + * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients. + * If there is a query string, a transaction gets logged into hbase, proving the application is connected to the data store. + * If there is no query string, no transacction logging is done to hbase. + * + * @param headers the headers + * @param req the req + * @param myAction if exists will cause transaction to be logged to hbase + * @return the response + */ + @GET + @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + @Path(echoPath) + public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req, + @QueryParam("action") String myAction) { + Response response = null; + + AAIException ex = null; + String fromAppId = null; + String transId = null; + + try { + fromAppId = getFromAppId(headers ); + transId = getTransId(headers); + } catch (AAIException e) { + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add("PUT uebProvider"); + templateVars.add("addTopic"); + return Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } + + try { + + HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>(); + + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(fromAppId); + templateVars.add(transId); + + exceptionList.put(new AAIException("AAI_0002", "OK"), templateVars); + + response = Response.status(Status.OK) + .entity(ErrorLogHelper.getRESTAPIInfoResponse( + headers.getAcceptableMediaTypes(), exceptionList)) + .build(); + + } catch (Exception e) { + ex = new AAIException("AAI_4000", e); + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(Action.GET.name()); + templateVars.add(fromAppId +" "+transId); + + response = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse( + headers.getAcceptableMediaTypes(), ex, + templateVars)).build(); + + } finally { + if (ex != null) { + ErrorLogHelper.logException(ex); + } + + } + + return response; + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/util/LogFormatTools.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/LogFormatTools.java new file mode 100644 index 0000000..d6fcd67 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/LogFormatTools.java @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.util; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class LogFormatTools { + + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern(DATE_FORMAT) + .withZone(ZoneOffset.UTC); + + public static String getCurrentDateTime() { + return DTF.format(ZonedDateTime.now()); + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/rest/util/ValidateEncoding.java b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/ValidateEncoding.java new file mode 100644 index 0000000..a09a317 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/rest/util/ValidateEncoding.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.rest.util; + +import java.io.UnsupportedEncodingException; +import java.net.URI; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; + +import org.springframework.web.util.UriUtils; + +/** + * The Class ValidateEncoding. + */ +public class ValidateEncoding { + + private final String encoding = "UTF-8"; + + /** + * Instantiates a new validate encoding. + */ + private ValidateEncoding() { + + } + + /** + * The Class Helper. + */ + private static class Helper { + + /** The Constant INSTANCE. */ + private static final ValidateEncoding INSTANCE = new ValidateEncoding(); + } + + /** + * Gets the single instance of ValidateEncoding. + * + * @return single instance of ValidateEncoding + */ + public static ValidateEncoding getInstance() { + return Helper.INSTANCE; + } + + /** + * Validate. + * + * @param uri the uri + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public boolean validate(URI uri) throws UnsupportedEncodingException { + boolean result = true; + if (!validatePath(uri.getRawPath())) { + result = false; + } + /*if (!validateQueryParams(uri.getRawQuery())) { + result = false; + } //TODO + */ + + return result; + } + + /** + * Validate. + * + * @param info the info + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public boolean validate(UriInfo info) throws UnsupportedEncodingException { + boolean result = true; + if (!validatePath(info.getPath(false))) { + result = false; + } + if (!validateQueryParams(info.getQueryParameters(false))) { + result = false; + } + + return result; + } + + /** + * Validate path. + * + * @param path the path + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean validatePath(String path) throws UnsupportedEncodingException { + String[] segments = path.split("/"); + boolean valid = true; + for (String segment : segments) { + if (!this.checkEncoding(segment)) { + valid = false; + } + } + + return valid; + + } + + /** + * Validate query params. + * + * @param params the params + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean validateQueryParams(MultivaluedMap<String, String> params) throws UnsupportedEncodingException { + boolean valid = true; + + for (String key : params.keySet()) { + if (!this.checkEncoding(key)) { + valid = false; + } + for (String item : params.get(key)) { + if (!this.checkEncoding(item)) { + valid = false; + } + } + } + return valid; + } + + /** + * Check encoding. + * + * @param segment the segment + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean checkEncoding(String segment) throws UnsupportedEncodingException { + boolean result = false; + String decode = UriUtils.decode(segment, encoding); + String encode = UriUtils.encode(decode, encoding); + result = segment.equals(encode); + + return result; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/transforms/Converter.java b/aai-traversal/src/main/java/org/openecomp/aai/transforms/Converter.java new file mode 100644 index 0000000..44a0222 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/transforms/Converter.java @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.transforms; + +public interface Converter { + String convert(String input); +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerCamelToLowerHyphenConverter.java b/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerCamelToLowerHyphenConverter.java new file mode 100644 index 0000000..a31da05 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerCamelToLowerHyphenConverter.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.transforms; + +import com.google.common.base.CaseFormat; + +public class LowerCamelToLowerHyphenConverter implements Converter { + + @Override + public String convert(String input) { + if(input == null){ + return null; + } + return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, input); + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerHyphenToLowerCamelConverter.java b/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerHyphenToLowerCamelConverter.java new file mode 100644 index 0000000..784adbe --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/transforms/LowerHyphenToLowerCamelConverter.java @@ -0,0 +1,81 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.transforms; + +/** + * <b>LowerHyphenToLowerCamelConverter</b> is the converter to use + * for converting from the lower hyphen to lower camel case + * <p> + * Examples: + * lower-test => lowerTest + * lower-Test => lowerTest + * lowerTest => lowerTest + * lower-test-val => lowerTestVal + * <p> + * + */ +public class LowerHyphenToLowerCamelConverter implements Converter { + + /** + * Converts the dash formatted string into a camel case string + * Ensure that the capitalization is not lost during this conversion + * <p> + * Loops through each character in the string + * checks if the current character is '-' and if it is then sets the + * boolean isPreviousCharDash to true and continues to the next iteration + * If the character is not '-', then checks if the previous character is dash + * If it is, then it will upper case the current character and appends to the builder + * Otherwise, it will just append the current character without any modification + * + * @param input the input string to convert to camel case + * @return a string that is converted to camel case + * if the input is null, then it returns null + */ + @Override + public String convert(String input) { + if(input == null){ + return null; + } + + int size = input.length(); + StringBuilder builder = new StringBuilder(size); + + boolean isPreviousCharDash = false; + + for(int index = 0; index < size; ++index){ + char ch = input.charAt(index); + + if(ch == '-'){ + isPreviousCharDash = true; + continue; + } + if(isPreviousCharDash){ + builder.append(Character.toUpperCase(ch)); + isPreviousCharDash = false; + } else{ + builder.append(ch); + } + } + + return builder.toString(); + } + +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/transforms/MapTraverser.java b/aai-traversal/src/main/java/org/openecomp/aai/transforms/MapTraverser.java new file mode 100644 index 0000000..7695240 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/transforms/MapTraverser.java @@ -0,0 +1,87 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.transforms; + + +import joptsimple.internal.Objects; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MapTraverser { + + private Converter converter; + + public MapTraverser(Converter converter){ + this.converter = converter; + } + + public Map<String, Object> convertKeys(Map<String, Object> map){ + + Objects.ensureNotNull(map); + + Map<String, Object> modifiedMap = new HashMap<String, Object>(); + convertKeys(map, modifiedMap); + + return modifiedMap; + } + + private Map<String, Object> convertKeys(Map<String, Object> original, Map<String, Object> modified){ + + for(Map.Entry<String, Object> entry : original.entrySet()){ + String key = entry.getKey(); + key = converter.convert(key); + Object value = entry.getValue(); + if(value instanceof Map){ + modified.put(key, convertKeys((Map<String, Object>)value, new HashMap<String, Object>())); + } else if(value instanceof List){ + modified.put(key, convertKeys((List<Object>) value)); + } else { + modified.put(key, value); + } + } + + return modified; + } + + public List<Object> convertKeys(List<Object> list){ + + List<Object> modifiedList = new ArrayList<Object>(); + if(list != null && list.size() > 0){ + + for(Object o : list){ + if(o instanceof Map){ + Map<String, Object> map = (Map<String, Object>) o; + modifiedList.add(convertKeys(map)); + } else if(o instanceof List){ + List<Object> l = (List<Object>) o; + modifiedList.add(convertKeys(l)); + } else { + modifiedList.add(o); + } + } + } + + return modifiedList; + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/util/AAIAppServletContextListener.java b/aai-traversal/src/main/java/org/openecomp/aai/util/AAIAppServletContextListener.java new file mode 100644 index 0000000..97b7edb --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/util/AAIAppServletContextListener.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.util; + +import java.io.IOException; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.activemq.broker.BrokerService; + +import org.openecomp.aai.dbmap.AAIGraph; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.ModelInjestor; +import org.openecomp.aai.logging.ErrorLogHelper; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +public class AAIAppServletContextListener implements ServletContextListener { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(AAIAppServletContextListener.class.getName()); + + private BrokerService broker = new BrokerService(); + + /** + * Destroys Context + * + * @param arg0 the ServletContextEvent + */ + public void contextDestroyed(ServletContextEvent arg0) { + LOGGER.info("AAIGraph shutting down"); + AAIGraph.getInstance().graphShutdown(); + LOGGER.info("AAIGraph shutdown"); + + try { + broker.stop(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Initializes Context + * + * @param arg0 the ServletContextEvent + */ + public void contextInitialized(ServletContextEvent arg0) { + System.setProperty("org.openecomp.aai.serverStarted", "false"); + LOGGER.info("***AAI Server initialization started..."); + + try { + LOGGER.info("Loading aaiconfig.properties"); + AAIConfig.init(); + + LOGGER.info("Loading error.properties"); + ErrorLogHelper.loadProperties(); + + LOGGER.info("Loading graph database"); + + AAIGraph.getInstance(); + ModelInjestor.getInstance(); + + // Jsm internal broker for aai events + broker = new BrokerService(); + broker.addConnector("tcp://localhost:61446"); + broker.setPersistent(false); + broker.setUseJmx(false); + broker.setSchedulerSupport(false); + broker.start(); + + LOGGER.info("A&AI Server initialization succcessful."); + System.setProperty("org.openecomp.aai.serverStarted", "true"); + + } catch (AAIException e) { + ErrorLogHelper.logException(e); + throw new RuntimeException("AAIException caught while initializing A&AI server", e); + } catch (IOException e) { + ErrorLogHelper.logError("AAI_4000", e.getMessage()); + throw new RuntimeException("IOException caught while initializing A&AI server", e); + } catch (Exception e) { + LOGGER.error("Unknown failure while initializing A&AI Server", e); + throw new RuntimeException("Unknown failure while initializing A&AI server", e); + } + } +} diff --git a/aai-traversal/src/main/java/org/openecomp/aai/util/RestURL.java b/aai-traversal/src/main/java/org/openecomp/aai/util/RestURL.java new file mode 100644 index 0000000..7c70929 --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/util/RestURL.java @@ -0,0 +1,704 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.util; + +import java.io.UnsupportedEncodingException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.springframework.web.util.UriUtils; + +import org.openecomp.aai.domain.model.AAIResource; +import org.openecomp.aai.domain.model.AAIResourceKey; +import org.openecomp.aai.domain.model.AAIResourceKeys; +import org.openecomp.aai.domain.model.AAIResources; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.extensions.AAIExtensionMap; +import org.openecomp.aai.ingestModel.DbMaps; +import org.openecomp.aai.ingestModel.IngestModelMoxyOxm; +import com.google.common.base.CaseFormat; +import com.thinkaurelius.titan.core.TitanEdge; +import com.thinkaurelius.titan.core.TitanTransaction; +import com.thinkaurelius.titan.core.TitanVertex; + +public class RestURL { + + + /* + * method returns a REST URL for the given node based on its nodetype and key + * information + */ + + /** + * Gets the. + * + * @param graph the graph + * @param node the node + * @param apiVersion the api version + * @param isLegacyVserverUEB the is legacy vserver UEB + * @param isCallbackurl the is callbackurl + * @return the string + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static String get(TitanTransaction graph, TitanVertex node, String apiVersion, Boolean isLegacyVserverUEB, Boolean isCallbackurl) throws AAIException, UnsupportedEncodingException + { + String nodeType = node.<String>property("aai-node-type").orElse(null); + String url = ""; + String currentNodeType = nodeType; + Boolean noMoreDependentNodes = true; + TitanVertex currentNode = node; + + // if the caller supplies an apiVersion we'll use it, otherwise we'll just + // reflect back from the called URI + if (apiVersion == null) { + apiVersion = AAIApiVersion.get(); + } + + String nodeURI = null; + if (Boolean.parseBoolean(AAIConfig.get("aai.use.unique.key", "false"))) + nodeURI = node.<String>property("aai-unique-key").orElse(null); + + if (nodeURI != null && !nodeURI.equals("")) { + if (isCallbackurl) { + url = AAIConfig.get(AAIConstants.AAI_GLOBAL_CALLBACK_URL) + apiVersion + "/" + nodeURI; + return url; + } else { + url = AAIApiServerURLBase.get() + apiVersion + "/" + nodeURI; + return url; + } + } + + // TODO + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + // add the url component for the dependent on nodes for the node passed in + while (noMoreDependentNodes) { + Collection <String> depNodeTypeColl = dbMaps.NodeDependencies.get(currentNodeType); + Iterator <String> depNodeTypeListIterator = (Iterator<String>) depNodeTypeColl.iterator(); + if (!depNodeTypeListIterator.hasNext()) { + noMoreDependentNodes = false; + break; + } + + // Look for IN edges for the current Node and find its Parent - and make it the current Node + boolean foundParent = false; + Iterator <Edge> inEdges = currentNode.edges(Direction.IN); + while( inEdges.hasNext() ){ + TitanEdge inEdge = (TitanEdge) inEdges.next(); + Boolean inEdgeIsParent = inEdge.<Boolean>property("isParent").orElse(null); + if( inEdgeIsParent != null && inEdgeIsParent ){ + foundParent = true; + currentNode = (TitanVertex) inEdge.otherVertex(currentNode); + break; + } + } + + if (foundParent == false) { + break; + } + + // find the key(s) and add to the url + // first see what type of node the parent is - note some nodes can have one of many kinds of parents + String depNodeType = currentNode.<String>property("aai-node-type").orElse(null); + Collection <String> keyProps = dbMaps.NodeKeyProps.get(depNodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + String nodeUrl = null; + String depNodeTypePlural = dbMaps.NodePlural.get(depNodeType); + + if (depNodeTypePlural != null) + { + nodeUrl = depNodeTypePlural + "/" + depNodeType + "/"; + } + + while (keyPropI.hasNext()) { + Object nodeKey = currentNode.<Object>property(keyPropI.next()).orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey.toString()) + "/"; + } + + currentNodeType = depNodeType; + + url = nodeUrl + url; + } + // use the name space of the highest level of unique node since lots of children node types + // are common ex. l-interface is in the path for pserver and vpe + String urlNamespace = dbMaps.NodeNamespace.get(currentNodeType) + "/"; + urlNamespace = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, urlNamespace); + + // add the url component for the node passed in + Collection <String> keyProps = dbMaps.NodeKeyProps.get(nodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + String nodeUrl = null; + String nodeTypePlural = ""; + nodeTypePlural = dbMaps.NodePlural.get(nodeType); + + + if (nodeTypePlural != null && !nodeTypePlural.equals("")){ + nodeUrl = nodeTypePlural + "/" + nodeType + "/"; + } else { + nodeUrl = nodeType + "/"; + } + + if (nodeType.equals("ipaddress")) { // this has 2 keys but API only uses port-or -address in URL + String nodeKey = node.<String>property("port-or-interface").orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey) + "/"; + } else { + while (keyPropI.hasNext()) { + Object nodeKey = node.<Object>property(keyPropI.next()).orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey.toString()) + "/"; + } + } + if (isCallbackurl) { + url = AAIConfig.get(AAIConstants.AAI_GLOBAL_CALLBACK_URL) + apiVersion + "/" + urlNamespace + url + nodeUrl; + } else { + url = AAIApiServerURLBase.get() + apiVersion + "/" + urlNamespace + url + nodeUrl; + } + return url; + } + + /** + * Gets the search url. + * + * @param graph the graph + * @param node the node + * @param apiVersion the api version + * @return the search url + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static String getSearchUrl(TitanTransaction graph, TitanVertex node, String apiVersion) throws AAIException, UnsupportedEncodingException + { + String nodeType = node.<String>property("aai-node-type").orElse(null); + String url = ""; + String currentNodeType = nodeType; + Boolean noMoreDependentNodes = true; + TitanVertex currentNode = node; + Boolean hasCloudRegion = false; + + // if the caller supplies an apiVersion we'll use it, otherwise we'll just + // reflect back from the called URI + if (apiVersion == null) { + apiVersion = AAIApiVersion.get(); + } + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP)); + + // add the url component for the dependent on nodes for the node passed in + while (noMoreDependentNodes) { + Collection <String> depNodeTypeColl = dbMaps.NodeDependencies.get(currentNodeType); + Iterator <String> depNodeTypeListIterator = (Iterator<String>) depNodeTypeColl.iterator(); + if (!depNodeTypeListIterator.hasNext()) { + noMoreDependentNodes = false; + break; + } + + // Look for IN edges for the current Node and find its Parent - and make it the current Node + boolean foundParent = false; + Iterator <Edge> inEdges = currentNode.edges(Direction.IN); + while( inEdges.hasNext() ){ + TitanEdge inEdge = (TitanEdge) inEdges.next(); + Boolean inEdgeIsParent = inEdge.<Boolean>property("isParent").orElse(null); + if( inEdgeIsParent != null && inEdgeIsParent ){ + foundParent = true; + currentNode = inEdge.otherVertex(currentNode); + break; + } + } + + if (foundParent == false) { + break; + } + + // find the key(s) and add to the url + // first see what type of node the parent is - note some nodes can have one of many kinds of parents + String depNodeType = currentNode.<String>property("aai-node-type").orElse(null); + Collection <String> keyProps = dbMaps.NodeKeyProps.get(depNodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + String nodeUrl = null; + String depNodeTypePlural = dbMaps.NodePlural.get(depNodeType); + + if (depNodeTypePlural != null) + nodeUrl = depNodeTypePlural + "/" + depNodeType + "/"; + + while (keyPropI.hasNext()) { + Object nodeKey = currentNode.<Object>property(keyPropI.next()).orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey.toString()) + "/"; + } + + currentNodeType = depNodeType; + + url = nodeUrl + url; + } + // use the name space of the highest level of unique node since lots of children node types + // are common ex. l-interface is in the path for pserver and vpe + String urlNamespace = dbMaps.NodeNamespace.get(currentNodeType) + "/"; + urlNamespace = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, urlNamespace); + + // add the url component for the node passed in + Collection <String> keyProps = dbMaps.NodeKeyProps.get(nodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + String nodeUrl = null; + String nodeTypePlural = ""; + nodeTypePlural = dbMaps.NodePlural.get(nodeType); + + if (nodeTypePlural != null && !nodeTypePlural.equals("")) + nodeUrl = nodeTypePlural + "/" + nodeType + "/"; + else + nodeUrl = nodeType + "/"; + + if (nodeType.equals("ipaddress")) { // this has 2 keys but API only uses port-or -address in URL + String nodeKey = node.<String>property("port-or-interface").orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey) + "/"; + } else { + while (keyPropI.hasNext()) { + Object nodeKey = node.<Object>property(keyPropI.next()).orElse(null); + nodeUrl += RestURLEncoder.encodeURL(nodeKey.toString()) + "/"; + } + } + + String nodeVersion = dbMaps.NodeVersionInfoMap.get(nodeType); + String urlVersion = null; + int nodeVerNum = Integer.parseInt(nodeVersion.substring(1)); + int apiVerNum = Integer.parseInt(apiVersion.substring(1)); + + if (hasCloudRegion) { + if (apiVerNum < 7) + urlVersion = "v7"; // or set to the latest version? + else + urlVersion = apiVersion; + } else { + if (nodeVerNum == apiVerNum || nodeVerNum < apiVerNum) + urlVersion = apiVersion; + else + urlVersion = nodeVersion; + } + url = AAIApiServerURLBase.get() + urlVersion + "/" + urlNamespace + url + nodeUrl; + //remove the trailing "/" + url = url.substring(0, url.length()-1); + return url; + } + + /** + * Gets the. + * + * @param graph the graph + * @param node the node + * @return the string + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static String get(TitanTransaction graph, TitanVertex node) throws AAIException, UnsupportedEncodingException + { + return get(graph, node, null, false, false); + } + + /** + * Gets the. + * + * @param graph the graph + * @param node the node + * @param apiVersion the api version + * @return the string + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static String get(TitanTransaction graph, TitanVertex node, String apiVersion) throws AAIException, UnsupportedEncodingException + { + return get(graph, node, apiVersion, false, false); + } + + /** + * Gets the. + * + * @param graph the graph + * @param node the node + * @param apiVersion the api version + * @param isLegacyVserverUEB the is legacy vserver UEB + * @return the string + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static String get(TitanTransaction graph, TitanVertex node, String apiVersion, Boolean isLegacyVserverUEB) throws AAIException, UnsupportedEncodingException + { + return get(graph, node, apiVersion, isLegacyVserverUEB, false); + } + + /** + * Gets the key hashes. + * + * @param graph the graph + * @param node the node + * @return the key hashes + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static LinkedHashMap<String, Object> getKeyHashes(TitanTransaction graph, TitanVertex node) throws AAIException, UnsupportedEncodingException + { + return getKeyHashes(graph, node, null); + } + + /* + * method returns a Hash of Hashes for each parents keys for the given node based on its nodetype + * Special cases for REST URLs: + * - old URLS for vserver, ipaddress and volume node types for v2/v3 + * - images, flavor, vnic and l-interface node types will return new url + * - nodetypes with multiple keys such as service capability + * - nodetypes with multiple keys such as ipaddress where we use one key in the URL + * - cvlan-tags and *list nodetypes - have special or no plurals - they get handled via the hash Map + */ + + /** + * Gets the key hashes. + * + * @param graph the graph + * @param node the node + * @param apiVersion the api version + * @return the key hashes + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static LinkedHashMap <String,Object> getKeyHashes(TitanTransaction graph, TitanVertex node, String apiVersion) throws AAIException, UnsupportedEncodingException + { + String nodeType = node.<String>property("aai-node-type").orElse(null); + Boolean noMoreDependentNodes = true; + TitanVertex currentNode = node; + + if (apiVersion == null || apiVersion.equals("")) { + apiVersion = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + } + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(apiVersion); + + // Hash of hashes of keys for each node and its ancestry + LinkedHashMap <String,Object> returnHash = new LinkedHashMap <String,Object> (); + + // create the hash for the keys for the node passed in + HashMap <String,Object> thisNodeHash = new HashMap <String,Object> (); + Collection <String> keyProps = dbMaps.NodeKeyProps.get(nodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + if (nodeType.equals("ipaddress")) { // this has 2 keys but API only uses port-or -address in URL + String nodeKeyValue = node.<String>property("port-or-interface").orElse(null); + thisNodeHash.put("port-or-interface", nodeKeyValue); + } else { + while (keyPropI.hasNext()) { + String nodeKeyName = keyPropI.next(); + Object nodeKeyValue = node.<Object>property(nodeKeyName).orElse(null); + thisNodeHash.put(nodeKeyName, nodeKeyValue); + nodeKeyName = nodeType + "." + nodeKeyName; + } + } + returnHash.putAll(thisNodeHash); + + // create and add the hashes for the dependent nodes for the node passed in + while (noMoreDependentNodes) { +// Collection <String> depNodeTypeColl = DbRules.NodeDependencies.get(currentNodeType); +// Iterator <String> depNodeTypeListIterator = (Iterator<String>) depNodeTypeColl.iterator(); + HashMap <String,Object> depNodeHash = new HashMap <String,Object> (); +// +// if (!depNodeTypeListIterator.hasNext()) { +// noMoreDependentNodes = false; +// break; +// } + + boolean foundParent = false; + + // Look for IN edges for the current Node and find its Parent - and make it the current Node + Iterator <Edge> inEdges = currentNode.edges(Direction.IN); + while( inEdges.hasNext() ){ + TitanEdge inEdge = (TitanEdge) inEdges.next(); + Boolean inEdgeIsParent = inEdge.<Boolean>property("isParent").orElse(null); + if( inEdgeIsParent != null && inEdgeIsParent ){ + currentNode = inEdge.otherVertex(currentNode); + foundParent = true; + break; + } + } + if (foundParent == false) { + break; + } + + // find the key(s) and add to the url + // first see what type of node the parent is - note some nodes can have one of many kinds of parents + String depNodeType = currentNode.<String>property("aai-node-type").orElse(null); + keyProps = dbMaps.NodeKeyProps.get(depNodeType); + keyPropI = keyProps.iterator(); + + while (keyPropI.hasNext()) { + String nodeKeyName = keyPropI.next(); + Object nodeKeyValue = currentNode.<Object>property(nodeKeyName).orElse(null); + nodeKeyName = depNodeType + "." + nodeKeyName; + // key name will be like tenant.tenant-id + + depNodeHash.put(nodeKeyName, nodeKeyValue); + } + returnHash.putAll(depNodeHash); + } + + return returnHash; + } + + /* + * method returns a Hash of Hashes for each parents keys for the given node based on its nodeURI + * Special cases for REST URLs: + * - images, flavor, vnic and l-interface node types will return new url + * - nodetypes with multiple keys such as service capability + * - nodetypes with multiple keys such as ipaddress where we use one key in the URL + * - cvlan-tags and *list nodetypes - have special or no plurals - they get handled via the hash Map + */ + + /** + * Gets the key hashes. + * + * @param nodeURI the node URI + * @return the key hashes + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static LinkedHashMap <String,Object> getKeyHashes(String nodeURI) throws AAIException, UnsupportedEncodingException + { + return getKeyHashes(nodeURI, null); + + } + + /** + * Gets the key hashes. + * + * @param nodeURI the node URI + * @param apiVersion the api version + * @return the key hashes + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public static LinkedHashMap <String,Object> getKeyHashes(String nodeURI, String apiVersion) throws AAIException, UnsupportedEncodingException + { + + if (apiVersion == null || apiVersion.equals("")) + apiVersion = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP); + + DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(apiVersion); + + // Hash of hashes of keys for each node and its ancestry + LinkedHashMap <String,Object> returnHash = new LinkedHashMap <String,Object> (); + + String path = nodeURI.replaceFirst("^/", ""); + Path p = Paths.get(path); + int index = p.getNameCount() - 2; // index of where we expect the node type to be + + // if the node type has one key + String currentNodeType = p.getName(index).toString(); + // if the node type has two keys - this assumes max 2 keys + if (!dbMaps.NodeKeyProps.containsKey(currentNodeType)) + currentNodeType = p.getName(--index).toString(); + + // create the hash for the keys for the node passed in + LinkedHashMap <String,Object> thisNodeHash = new LinkedHashMap <String,Object> (); + Collection <String> keyProps = dbMaps.NodeKeyProps.get(currentNodeType); + Iterator <String> keyPropI = keyProps.iterator(); + + if (currentNodeType.equals("ipaddress")) { // this has 2 keys but API only uses port-or -address in URL + String nodeKeyValue = p.getName(index + 1).toString(); + thisNodeHash.put("port-or-interface", nodeKeyValue); + } else { + int j = 1; + while (keyPropI.hasNext()) { + String nodeKeyName = currentNodeType + "." + keyPropI.next(); + String nodeKeyValue = p.getName(index + j++).toString(); + thisNodeHash.put(nodeKeyName, nodeKeyValue); + } + } + returnHash.putAll(thisNodeHash); + if (!currentNodeType.contains("-list")) + index -= 3; + else + index -= 2; // no plural in this case + + + // create and add the hashes for the dependent nodes for the node passed in + LinkedHashMap <String,Object> depNodeHash = new LinkedHashMap <String,Object> (); + String depNodeType = null; + while (index >= 2) { + if (depNodeType == null) depNodeType = p.getName(index).toString(); + //System.out.println("index=" + index); + // if the node type has one key + currentNodeType = p.getName(index).toString(); + // if the node type has two keys - this assumes max 2 keys + if (!dbMaps.NodeKeyProps.containsKey(currentNodeType)) + currentNodeType = p.getName(--index).toString(); + + keyProps = dbMaps.NodeKeyProps.get(currentNodeType); + keyPropI = keyProps.iterator(); + + if (currentNodeType.equals("ipaddress")) { // this has 2 keys but API only uses port-or -address in URL + String nodeKeyValue = p.getName(index + 1).toString(); + depNodeHash.put("port-or-interface", nodeKeyValue); + } else { + int j = 1; + while (keyPropI.hasNext()) { + String nodeKeyName = currentNodeType + "." + keyPropI.next(); + String nodeKeyValue = p.getName(index + j++).toString(); + depNodeHash.put(nodeKeyName, nodeKeyValue); + } + } + + if (!currentNodeType.contains("-list")) + index -= 3; + else + index -= 2; // no plural in this case + } + if (depNodeType != null) + returnHash.putAll(depNodeHash); + + return returnHash; + } + + /** + * Parses the uri. + * + * @param allKeys the all keys + * @param keyList the key list + * @param uri the uri + * @param aaiExtMap the aai ext map + * @return the AAI resource + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public static AAIResource parseUri(HashMap<String, String> allKeys, LinkedHashMap<String, + LinkedHashMap<String,Object>> keyList, String uri, + AAIExtensionMap aaiExtMap) throws UnsupportedEncodingException, AAIException { + + String[] ps = uri.split("/"); + + String apiVersion = ps[0]; + aaiExtMap.setApiVersion(apiVersion); + + AAIResources aaiResources = org.openecomp.aai.ingestModel.IngestModelMoxyOxm.aaiResourceContainer.get(apiVersion); + + String namespace = ps[1]; + + aaiExtMap.setNamespace(namespace); + + // /vces/vce/{vnf-id}/port-groups/port-group/{port-group-id}/cvlan-tag-entry/cvlan-tag/{cvlan-tag} + + // FullName -> /Vces/Vce/PortGroups/PortGroup/CvlanTagEntry/CvlanTag <- + + String fullResourceName = "/" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, namespace); + AAIResources theseResources = new AAIResources(); + + StringBuffer thisUri = new StringBuffer(); + + // the URI config option in the props file has a trailing slash + thisUri.append("/" + namespace); + + boolean firstNode = true; + + AAIResource lastResource = null; + + for (int i = 2; i < ps.length; i++) { + + AAIResource aaiRes; + StringBuffer tmpResourceName = new StringBuffer(); + + String p = ps[i]; + String seg =ps[i]; + + thisUri.append("/" + seg); + + tmpResourceName.append(fullResourceName); + + if (seg.equals("cvlan-tag")) { + seg = "cvlan-tag-entry"; + } + tmpResourceName.append("/" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, seg)); + + String tmpResource = tmpResourceName.toString(); + + if (aaiResources.getAaiResources().containsKey(tmpResource)) { + aaiRes = aaiResources.getAaiResources().get(tmpResource); + lastResource = aaiRes; + theseResources.getAaiResources().put(tmpResource, aaiRes); + fullResourceName = tmpResource; + if ("node".equals(aaiRes.getResourceType())) { + + if (firstNode == true) { + aaiExtMap.setTopObjectFullResourceName(fullResourceName); + firstNode = false; + } + + // get the keys, which will be in order and the next path segment(s) + AAIResourceKeys keys = aaiRes.getAaiResourceKeys(); + + LinkedHashMap<String,Object> subKeyList = new LinkedHashMap<String,Object>(); + + // there might not be another path segment + if ( (i + 1) < ps.length) { + + for (AAIResourceKey rk : keys.getAaiResourceKey()) { + String p1 = ps[++i]; + String encodedKey = p1.toString(); + thisUri.append("/" + encodedKey); + String decodedKey = UriUtils.decode(p1.toString(), "UTF-8"); + subKeyList.put(rk.getKeyName(), decodedKey); + } + keyList.put(tmpResource, subKeyList); + // this is the key + allKeys.put(tmpResource, thisUri.toString()); + } + } else { // examples sit directly under the container level, should probably be query params!!! + if ( (i + 1) < ps.length) { + String p1 = ps[i+1]; + if (p1.toString().equals("example") || p1.toString().equals("singletonExample")) { + LinkedHashMap<String,Object> subKeyList = new LinkedHashMap<String,Object>(); + subKeyList.put("container|example", p1.toString()); + keyList.put(tmpResource, subKeyList); + } + } + } + } else { + if (p.equals("relationship-list")) { + LinkedHashMap<String,Object> subKeyList = new LinkedHashMap<String,Object>(); + subKeyList.put("container|relationship", p.toString()); + keyList.put(tmpResource, subKeyList); + } else if ( p.toString().length() > 0 && !p.toString().equals("example") && !p.toString().equals("singletonExample") + && !p.toString().equals("relationship") ) { + // this means the URL will break the model, so we bail + throw new AAIException("AAI_3001", "bad path"); + } + } + } + aaiExtMap.setUri(AAIConfig.get("aai.global.callback.url") + apiVersion + thisUri.toString()); + aaiExtMap.setNotificationUri(AAIConfig.get("aai.global.callback.url") + AAIConfig.get("aai.notification.current.version") + thisUri.toString()); + aaiExtMap.setFullResourceName(fullResourceName); + return lastResource; + } + +} + diff --git a/aai-traversal/src/main/java/org/openecomp/aai/util/StoreNotificationEvent.java b/aai-traversal/src/main/java/org/openecomp/aai/util/StoreNotificationEvent.java new file mode 100644 index 0000000..996abba --- /dev/null +++ b/aai-traversal/src/main/java/org/openecomp/aai/util/StoreNotificationEvent.java @@ -0,0 +1,358 @@ +/*- + * ============LICENSE_START======================================================= + * org.openecomp.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.aai.util; + +import java.io.StringWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.xml.bind.Marshaller; + +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.PhaseInterceptorChain; +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.json.JSONException; +import org.json.JSONObject; + +import org.openecomp.aai.dmaap.AAIDmaapEventJMSProducer; +import org.openecomp.aai.domain.notificationEvent.NotificationEvent; +import org.openecomp.aai.exceptions.AAIException; +import org.openecomp.aai.introspection.Introspector; +import org.openecomp.aai.introspection.Loader; +import org.openecomp.aai.introspection.exceptions.AAIUnknownObjectException; + +public class StoreNotificationEvent { + + private AAIDmaapEventJMSProducer messageProducer; + private String fromAppId = ""; + private String transId = ""; + + /** + * Instantiates a new store notification event. + */ + public StoreNotificationEvent() { + this.messageProducer = new AAIDmaapEventJMSProducer(); + Message inMessage = PhaseInterceptorChain.getCurrentMessage().getExchange().getInMessage(); + Map<String, List<String>> headersList = CastUtils.cast((Map<?, ?>) inMessage.get(Message.PROTOCOL_HEADERS)); + if (headersList != null) { + List<String> xt = headersList.get("X-TransactionId"); + if (xt != null) { + for (String transIdValue : xt) { + transId = transIdValue; + } + } + List<String> fa = headersList.get("X-FromAppId"); + if (fa != null) { + for (String fromAppIdValue : fa) { + + fromAppId = fromAppIdValue; + } + } + } + } + + /** + * Store event. + * + * @param eh + * the eh + * @param obj + * the obj + * @throws AAIException + * the AAI exception + */ + public void storeEvent(NotificationEvent.EventHeader eh, Object obj) throws AAIException { + + if (obj == null) { + throw new AAIException("AAI_7350"); + } + + org.openecomp.aai.domain.notificationEvent.ObjectFactory factory = new org.openecomp.aai.domain.notificationEvent.ObjectFactory(); + + org.openecomp.aai.domain.notificationEvent.NotificationEvent ne = factory.createNotificationEvent(); + + if (eh.getId() == null) { + eh.setId(genDate2() + "-" + UUID.randomUUID().toString()); + } + if (eh.getTimestamp() == null) { + eh.setTimestamp(genDate()); + } + + // there's no default, but i think we want to put this in hbase? + + if (eh.getEntityLink() == null) { + eh.setEntityLink("UNK"); + } + + if (eh.getAction() == null) { + eh.setAction("UNK"); + } + + if (eh.getEventType() == null) { + eh.setEventType(AAIConfig.get("aai.notificationEvent.default.eventType", "UNK")); + } + + if (eh.getDomain() == null) { + eh.setDomain(AAIConfig.get("aai.notificationEvent.default.domain", "UNK")); + } + + if (eh.getSourceName() == null) { + eh.setSourceName(AAIConfig.get("aai.notificationEvent.default.sourceName", "UNK")); + } + + if (eh.getSequenceNumber() == null) { + eh.setSequenceNumber(AAIConfig.get("aai.notificationEvent.default.sequenceNumber", "UNK")); + } + + if (eh.getSeverity() == null) { + eh.setSeverity(AAIConfig.get("aai.notificationEvent.default.severity", "UNK")); + } + + if (eh.getVersion() == null) { + eh.setVersion(AAIConfig.get("aai.notificationEvent.default.version", "UNK")); + } + + ne.setCambriaPartition(AAIConstants.UEB_PUB_PARTITION_AAI); + ne.setEventHeader(eh); + ne.setEntity(obj); + + try { + PojoUtils pu = new PojoUtils(); + String entityJson = pu.getJsonFromObject(ne); + sendToDmaapJmsQueue(entityJson); + } catch (Exception e) { + throw new AAIException("AAI_7350", e); + } + } + + /** + * Store dynamic event. + * + * @param notificationJaxbContext + * the notification jaxb context + * @param notificationVersion + * the notification version + * @param eventHeader + * the event header + * @param obj + * the obj + * @throws AAIException + * the AAI exception + */ + public void storeDynamicEvent(DynamicJAXBContext notificationJaxbContext, String notificationVersion, DynamicEntity eventHeader, DynamicEntity obj) throws AAIException { + + if (obj == null) { + throw new AAIException("AAI_7350"); + } + + DynamicEntity notificationEvent = notificationJaxbContext.getDynamicType("inventory.aai.inventory.org." + notificationVersion + ".NotificationEvent").newDynamicEntity(); + + if (eventHeader.get("id") == null) { + eventHeader.set("id", genDate2() + "-" + UUID.randomUUID().toString()); + } + + if (eventHeader.get("timestamp") == null) { + eventHeader.set("timestamp", genDate()); + } + + if (eventHeader.get("entityLink") == null) { + eventHeader.set("entityLink", "UNK"); + } + + if (eventHeader.get("action") == null) { + eventHeader.set("action", "UNK"); + } + + if (eventHeader.get("eventType") == null) { + eventHeader.set("eventType", AAIConfig.get("aai.notificationEvent.default.eventType", "UNK")); + } + + if (eventHeader.get("domain") == null) { + eventHeader.set("domain", AAIConfig.get("aai.notificationEvent.default.domain", "UNK")); + } + + if (eventHeader.get("sourceName") == null) { + eventHeader.set("sourceName", AAIConfig.get("aai.notificationEvent.default.sourceName", "UNK")); + } + + if (eventHeader.get("sequenceNumber") == null) { + eventHeader.set("sequenceNumber", AAIConfig.get("aai.notificationEvent.default.sequenceNumber", "UNK")); + } + + if (eventHeader.get("severity") == null) { + eventHeader.set("severity", AAIConfig.get("aai.notificationEvent.default.severity", "UNK")); + } + + if (eventHeader.get("version") == null) { + eventHeader.set("version", AAIConfig.get("aai.notificationEvent.default.version", "UNK")); + } + + if (notificationEvent.get("cambriaPartition") == null) { + notificationEvent.set("cambriaPartition", AAIConstants.UEB_PUB_PARTITION_AAI); + } + + notificationEvent.set("eventHeader", eventHeader); + notificationEvent.set("entity", obj); + + try { + StringWriter result = new StringWriter(); + + Marshaller marshaller = notificationJaxbContext.createMarshaller(); + marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.MEDIA_TYPE, "application/json"); + marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_INCLUDE_ROOT, false); + marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, false); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false); + marshaller.marshal(notificationEvent, result); + + this.sendToDmaapJmsQueue(result.toString()); + + } catch (Exception e) { + throw new AAIException("AAI_7350", e); + } + } + + public void storeEvent(Loader loader, Introspector eventHeader, Introspector obj) throws AAIException { + if (obj == null) { + throw new AAIException("AAI_7350"); + } + + try { + final Introspector notificationEvent = loader.introspectorFromName("notification-event"); + + if (eventHeader.getValue("id") == null) { + eventHeader.setValue("id", genDate2() + "-" + UUID.randomUUID().toString()); + } + + if (eventHeader.getValue("timestamp") == null) { + eventHeader.setValue("timestamp", genDate()); + } + + if (eventHeader.getValue("entity-link") == null) { + eventHeader.setValue("entity-link", "UNK"); + } + + if (eventHeader.getValue("action") == null) { + eventHeader.setValue("action", "UNK"); + } + + if (eventHeader.getValue("event-type") == null) { + eventHeader.setValue("event-type", AAIConfig.get("aai.notificationEvent.default.eventType", "UNK")); + } + + if (eventHeader.getValue("domain") == null) { + eventHeader.setValue("domain", AAIConfig.get("aai.notificationEvent.default.domain", "UNK")); + } + + if (eventHeader.getValue("source-name") == null) { + eventHeader.setValue("source-name", AAIConfig.get("aai.notificationEvent.default.sourceName", "UNK")); + } + + if (eventHeader.getValue("sequence-number") == null) { + eventHeader.setValue("sequence-number", AAIConfig.get("aai.notificationEvent.default.sequenceNumber", "UNK")); + } + + if (eventHeader.getValue("severity") == null) { + eventHeader.setValue("severity", AAIConfig.get("aai.notificationEvent.default.severity", "UNK")); + } + + if (eventHeader.getValue("version") == null) { + eventHeader.setValue("version", AAIConfig.get("aai.notificationEvent.default.version", "UNK")); + } + + if (notificationEvent.getValue("cambria-partition") == null) { + notificationEvent.setValue("cambria-partition", AAIConstants.UEB_PUB_PARTITION_AAI); + } + + notificationEvent.setValue("event-header", eventHeader.getUnderlyingObject()); + notificationEvent.setValue("entity", obj.getUnderlyingObject()); + + String entityJson = notificationEvent.marshal(false); + sendToDmaapJmsQueue(entityJson); + } catch (JSONException e) { + throw new AAIException("AAI_7350", e); + } catch (AAIUnknownObjectException e) { + throw new AAIException("AAI_7350", e); + } + } + + private void sendToDmaapJmsQueue(String entityString) throws JSONException { + + JSONObject entityJsonObject = new JSONObject(entityString); + + JSONObject entityJsonObjectUpdated = new JSONObject(); + JSONObject finalJson = new JSONObject(); + + JSONObject entityHeader = entityJsonObject.getJSONObject("event-header"); + String cambriaPartition = entityJsonObject.getString("cambria.partition"); + + entityJsonObject.remove("event-header"); + entityJsonObject.remove("cambria.partition"); + + entityJsonObjectUpdated.put("event-header", entityHeader); + entityJsonObjectUpdated.put("cambria.partition", cambriaPartition); + + Iterator<String> iter = entityJsonObject.keys(); + JSONObject entity = new JSONObject(); + if (iter.hasNext()) { + entity = entityJsonObject.getJSONObject(iter.next()); + } + + entityJsonObjectUpdated.put("entity", entity); + + finalJson.put("event-topic", "AAI-EVENT"); + finalJson.put("transId", transId); + finalJson.put("fromAppId", fromAppId); + finalJson.put("fullId", ""); + finalJson.put("aaiEventPayload", entityJsonObjectUpdated); + + messageProducer.sendMessageToDefaultDestination(finalJson); + } + + /** + * Gen date. + * + * @return the string + */ + public static String genDate() { + Date date = new Date(); + DateFormat formatter = new SimpleDateFormat("YYYYMMdd-HH:mm:ss:SSS"); + return formatter.format(date); + } + + /** + * Gen date 2. + * + * @return the string + */ + public static String genDate2() { + Date date = new Date(); + DateFormat formatter = new SimpleDateFormat("YYYYMMddHHmmss"); + return formatter.format(date); + } + +} |