Update the license for 2017-2018 license
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / dbgraphgen / ModelBasedProcessing.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aai.dbgraphgen;
21
22 import com.att.eelf.configuration.EELFLogger;
23 import com.att.eelf.configuration.EELFManager;
24 import com.google.common.collect.ArrayListMultimap;
25 import com.google.common.collect.Multimap;
26 import com.google.common.util.concurrent.SimpleTimeLimiter;
27 import com.google.common.util.concurrent.TimeLimiter;
28 import com.google.common.util.concurrent.UncheckedTimeoutException;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
30 import org.apache.tinkerpop.gremlin.structure.Vertex;
31 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
32 import org.onap.aai.db.DbMethHelper;
33 import org.onap.aai.db.props.AAIProperties;
34 import org.onap.aai.dbgen.PropertyLimitDesc;
35 import org.onap.aai.exceptions.AAIException;
36 import org.onap.aai.introspection.Introspector;
37 import org.onap.aai.introspection.Loader;
38 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
39 import org.onap.aai.query.builder.QueryBuilder;
40 import org.onap.aai.schema.enums.PropertyMetadata;
41 import org.onap.aai.serialization.db.DBSerializer;
42 import org.onap.aai.serialization.db.EdgeRules;
43 import org.onap.aai.serialization.db.EdgeType;
44 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
45 import org.onap.aai.util.AAIConfig;
46
47 import java.util.*;
48 import java.util.concurrent.Callable;
49 import java.util.concurrent.TimeUnit;
50
51 /**
52  * Utility class that uses Model/Named-Query definitions to navigate the graph.   
53  */
54 public class ModelBasedProcessing {
55
56         private EELFLogger LOGGER = EELFManager.getInstance().getLogger(ModelBasedProcessing.class);
57         private final int MAX_LEVELS = 50;  // max depth allowed for our model - to protect against infinite loop problems
58
59         private TransactionalGraphEngine engine;
60         private Loader loader;
61         private DBSerializer serializer;
62         private DbMethHelper dbMethHelper;
63         
64         protected ModelBasedProcessing() {
65                 
66         }
67         public ModelBasedProcessing(Loader loader, TransactionalGraphEngine engine, DBSerializer serializer) {
68                 this.loader = loader;
69                 this.engine = engine;
70                 this.serializer = serializer;
71                 dbMethHelper = new DbMethHelper(loader, engine);
72         }
73         /**
74          * Gets the start nodes and model-ver's.
75          *
76          * @param transId the trans id
77          * @param fromAppId the from app id
78          * @param passedModelVersionId the passed model-version-id -- optional (unique id for a model-ver)
79          * @param passedModelId the passed model-invariant-id -- optional
80          * @param passedModelName the passed model-name -- optional
81          * @param passedTopNodeType the passed top node type -- optional (needed if neither model=invariant-id nor model-version-id is passed)
82          * @param startNodeFilterArrayOfHashes the start node filter array of hashes -- optional (used to locate the first node(s) of instance data)
83          * @param apiVer the api ver
84          * @return HashMap of startNodes and their corresponding model-version-id's
85          * @throws AAIException the AAI exception
86          */
87         public Map<String,String> getStartNodesAndModVersionIds( String transId, String fromAppId,
88                         String passedModelVersionId, 
89                         String passedModelInvId,
90                         String passedModelName,
91                         String passedTopNodeType,
92                         List<Map<String,Object>> startNodeFilterArrayOfHashes, 
93                         String apiVer ) 
94                                         throws AAIException {
95                 // ----------------------------------------------------------------------------------------------------
96                 // Get a hash for all start-nodes (key = vtxId, val = modelVersionId that applies)
97                 //     If no start-node-key info is passed, then use either the passed modelVersion or 
98                 //         the passed model-invariant-id or model-name to collect them.
99                 //     If start-node-key info is given, use it instead to look for start-nodes. 
100                 //         Note: if ONLY start-node-key info is given, then it would have to map to nodes which 
101                 //         have persona data.  Otherwise we'd have no way to know what model to collect data with.
102                 // ----------------------------------------------------------------------------------------------------
103
104                 Iterator<Vertex> startVerts = null;
105                 Map<String, String> startVertInfo = new HashMap<>();
106                 
107                 if( startNodeFilterArrayOfHashes.isEmpty() ){
108                         // Since they did not give any data to find start instances, we will have to find them
109                         // using whatever model-info they provided so we can use it to map to persona-data in the db.
110                         if( (passedModelVersionId == null || passedModelVersionId.equals(""))
111                                          && (passedModelInvId == null || passedModelInvId.equals(""))
112                                          && (passedModelName == null || passedModelName.equals(""))){
113                                 throw new AAIException("AAI_6118", "ModelInvariantId or ModelName or ModelVersionId required if no startNodeFilter data passed.");
114                         }
115                         else {
116                                 // Use whatever model info they pass to find start-node instances
117                                 // Get the first/top named-query-element used by this query
118                                 if( passedModelVersionId != null && !passedModelVersionId.equals("") ){
119                                         // Need to look up the model-invariant-id and model-version to check against persona data
120                                         Vertex modVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver",
121                                                         "model-version-id", passedModelVersionId);
122                                         Vertex modVtx = getModelGivenModelVer( modVerVtx, "" );
123                                         String calcModId = modVtx.<String>property("model-invariant-id").orElse(null);
124                                         // Now we can look up instances that match this model's info
125                                         if( calcModId != null ){
126                                                 startVerts = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(addDBAliasedSuffix("model-invariant-id"),calcModId).has(addDBAliasedSuffix("model-version-id"),passedModelVersionId);
127                                         }
128                                 }       
129                                 else if( passedModelInvId != null && !passedModelInvId.equals("") ){
130                                         // They gave us the model-invariant-id
131                                         startVerts = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(addDBAliasedSuffix("model-invariant-id"),passedModelInvId);
132                                 }
133                                 else if( passedModelName != null && !passedModelName.equals("") ){
134                                         List<Vertex> modelVerVtxList = getModelVersUsingName(transId, fromAppId, passedModelName);
135                                         List<Vertex> startVtxList = new ArrayList<>();
136                                         // Need to look up the model-inv-ids and model-versions to check against persona data
137                                         if( !modelVerVtxList.isEmpty() ){
138                                                 for( int i = 0; i < modelVerVtxList.size(); i++ ){
139                                                         String calcModVerId = (modelVerVtxList.get(i)).<String>property("model-version-id").orElse(null);
140                                                         Vertex modVtx = getModelGivenModelVer(modelVerVtxList.get(i),"");
141                                                         String calcModInvId = modVtx.<String>property("model-invariant-id").orElse(null);               
142                                                         // Now we can look up instances that match this model's info
143                                                         Iterator<Vertex> tmpStartIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(addDBAliasedSuffix("model-invariant-id"),calcModInvId).has(addDBAliasedSuffix("model-version-id"),calcModVerId);
144                                                         while( tmpStartIter.hasNext() ){
145                                                                 Vertex tmpStartVert = (Vertex) tmpStartIter.next();
146                                                                 startVtxList.add(tmpStartVert);
147                                                         }
148                                                 }
149                                         }
150                                         if( !startVtxList.isEmpty() ){
151                                                 startVerts = startVtxList.iterator();
152                                         }
153                                 }       
154                         }
155                         
156                         if( startVerts != null ){ 
157                                 while( startVerts.hasNext() ){
158                                         Vertex tmpStartVert = (Vertex) startVerts.next();
159                                         String vid = tmpStartVert.id().toString();
160                                         String tmpModId =  tmpStartVert.<String>property(addDBAliasedSuffix("model-invariant-id")).orElse(null);
161                                         String tmpModVerId =  tmpStartVert.<String>property(addDBAliasedSuffix("model-version-id")).orElse(null);
162                                         startVertInfo.put(vid, tmpModVerId);
163                                 }
164                         }
165                         if( startVertInfo.isEmpty() ){
166                                 throw new AAIException("AAI_6114", "Start Node(s) could not be found for model data passed.  " +
167                                                 "(modelVersionId = [" + passedModelVersionId + 
168                                                 "], modelInvariantId = [" + passedModelInvId +
169                                                 "], modelName = [" + passedModelName +
170                                                 "])");
171                         }
172                         
173                         return startVertInfo;
174                 }
175                 else {
176                         // Use start-node filter info to find start-node(s) - Note - there could also be model info passed that we'll need
177                         //     to use to trim down the set of start-nodes that we find based on the startNodeFilter data.
178                         String modTopNodeType ="";
179                         String modInfoStr = "";
180                         if( passedModelVersionId != null && !passedModelVersionId.equals("") ){
181                                 modTopNodeType = getModelVerTopWidgetType( transId, fromAppId, passedModelVersionId, "", "" );
182                                 modInfoStr = "modelVersionId = (" + passedModelVersionId + ")"; 
183                         }
184                         else if( passedModelInvId != null && !passedModelInvId.equals("") ){
185                                 modTopNodeType = getModelVerTopWidgetType( transId, fromAppId,"", passedModelInvId, "" );
186                                 modInfoStr = "modelId = (" + passedModelInvId + ")"; 
187                         }
188                         else if( passedModelName != null && !passedModelName.equals("") ){
189                                 modTopNodeType = getModelVerTopWidgetType( transId, fromAppId,"", "", passedModelName );
190                                 modInfoStr = "modelName = (" + passedModelName + ")"; 
191                         }
192                         
193                         if( modTopNodeType.equals("") ){
194                                 if( (passedTopNodeType == null) || passedTopNodeType.equals("") ){
195                                         String msg = "Could not determine the top-node nodeType for this request. modelInfo: [" + modInfoStr + "]";
196                                         throw new AAIException("AAI_6118", msg);
197                                 }
198                                 else {
199                                         // We couldn't find a top-model-type based on passed in model info, but they
200                                         // gave us a type to use -- so use it.
201                                         modTopNodeType = passedTopNodeType;
202                                 }
203                         }
204                         else {
205                                 // we did get a topNode type based on model info - make sure it doesn't contradict 
206                                 // the passsed-in one (if there is one)
207                                 if( passedTopNodeType != null && !passedTopNodeType.equals("") 
208                                                 && !passedTopNodeType.equals(modTopNodeType) ){
209                                         throw new AAIException("AAI_6120", "topNodeType passed in [" + passedTopNodeType
210                                                         + "] does not match nodeType derived for model info passed in: ["
211                                                         + modTopNodeType + "]"); 
212                                 }
213                         }
214                                 
215                         List<String> modelVersionIds2Check = new ArrayList<>();
216                         if( (passedModelName != null && !passedModelName.equals("")) ){
217                                 // They passed a modelName, so find all the model UUIDs (model-version-id's) that map to this
218                                 modelVersionIds2Check = getModelVerIdsUsingName(transId, fromAppId, passedModelName);
219                         }
220                         if( (passedModelVersionId != null && !passedModelVersionId.equals("")) ){
221                                 // They passed in a modelNameVersionId
222                                 if( modelVersionIds2Check.isEmpty() ){
223                                         // There was no modelName passed, so we can use the passed modelNameVersionId
224                                         modelVersionIds2Check.add(passedModelVersionId);
225                                 }
226                                 else if( modelVersionIds2Check.contains(passedModelVersionId) ){
227                                         // The passed in uuid does not conflict with what we got using the passed-in modelName.
228                                         // We'll just use the passed in uuid in this case.
229                                         // Hopefully they would not be passing strange combinations like this, but we'll try to deal with it.
230                                         modelVersionIds2Check = new ArrayList<>();  // Clear out what we had
231                                         modelVersionIds2Check.add(passedModelVersionId);
232                                 }
233                         }
234                         
235                         // We should now be OK with our topNodeType for this request, so we can look for the actual startNodes
236                         for( int i=0; i < startNodeFilterArrayOfHashes.size(); i++ ){
237                                 // Locate the starting node which will be used to look which corresponds to this set of filter data
238                                 Vertex startVtx = null;
239                                 try {
240                                         Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(modTopNodeType, startNodeFilterArrayOfHashes.get(i));
241                                         if (!result.isPresent()) {
242                                                 throw new AAIException("AAI_6114", "No Node of type " + modTopNodeType + " found for properties");
243                                         }
244                                         startVtx = result.get();
245                                 }
246                                 catch( AAIException e ){
247                                         String msg = "Could not find startNode of type = [" + modTopNodeType +  "], given these params: "  
248                                                         + startNodeFilterArrayOfHashes.get(i) + ". msg # from getUniqueNode() = " + e.getMessage();
249                                         throw new AAIException("AAI_6114", msg);
250                                 }
251                         
252                                 String vid = startVtx.id().toString();
253                                 String personaModInvId = startVtx.<String>property(addDBAliasedSuffix("model-invariant-id")).orElse(null);
254                                 String personaModVerId = startVtx.<String>property(addDBAliasedSuffix("model-version-id")).orElse(null);
255                                         
256                                 // Either this start-node has persona info (which should not contradict any passed-in model info)
257                                 //    or they should have passed in the model to use - so we'd just use that.
258                                 if( personaModVerId != null && !personaModVerId.equals("") ){
259                                         // There is persona data in this start-node.  So make sure it doesn't contradict any "passed" stuff
260                                         if( modelVersionIds2Check.isEmpty()  
261                                                         && (passedModelInvId == null || passedModelInvId.equals("")) ){
262                                                 // They didn't pass any model info, so use the persona one.
263                                                 startVertInfo.put(vid, personaModVerId);
264                                         }
265                                         else if( modelVersionIds2Check.isEmpty() 
266                                                         && (passedModelInvId != null && !passedModelInvId.equals("")) ){
267                                                 // They passed in just the modelId - so check it
268                                                 if( passedModelInvId.equals(personaModInvId) ){
269                                                         startVertInfo.put(vid, personaModVerId);
270                                                 }
271                                         }
272                                         else if( !modelVersionIds2Check.isEmpty() 
273                                                         && (passedModelInvId == null || passedModelInvId.equals("")) ){
274                                                 // They passed in just modelVersionId - so check
275                                                 if( modelVersionIds2Check.contains(personaModVerId) ){
276                                                         startVertInfo.put(vid, personaModVerId);
277                                                 }
278                                         }       
279                                         else if( !modelVersionIds2Check.isEmpty() 
280                                                         && (passedModelInvId != null && !passedModelInvId.equals("")) ){
281                                                 // We have BOTH a modelVersionIds and a modelId to check 
282                                                 if( passedModelInvId.equals(personaModInvId) 
283                                                                 && modelVersionIds2Check.contains(personaModVerId) ){
284                                                         startVertInfo.put(vid, personaModVerId);
285                                                 }
286                                         }
287                                 }
288                                 else {
289                                         // This start node did not have persona info -- so we will use the passed in model info if they passed one
290                                         if( passedModelVersionId!= null && !passedModelVersionId.equals("") ){
291                                                 // The model-version-id uniquely identifies a model-ver, so we can use it.
292                                                 startVertInfo.put(vid, passedModelVersionId);
293                                         }
294                                         else {
295                                                 throw new AAIException("AAI_6118", "Found startNode but since it does not have persona data, the " +
296                                                                 " model-version-id is required. "); 
297                                         }
298                                 }
299                         }
300                 }
301                 
302                 return startVertInfo;
303                 
304         }//end of  getStartNodesAndModVersionIds()
305                 
306
307         /**
308          * Query by model. (really model-ver)
309          *
310          * @param transId the trans id
311          * @param fromAppId the from app id
312          * @param modelVersionId the model-version-id (unique id in model-ver)
313          * @param modelInvariantId the model-invariant-id (unique id in model)
314          * @param modelName the model name
315          * @param topNodeType - optional (needed if neither model-invariant-id nor model-version-id is passed)
316          * @param startNodeFilterArrayOfHashes the start node filter array of hashes -- optional (used to locate the first node(s) of instance data)
317          * @param apiVer the api ver
318          * @return resultSet
319          * @throws AAIException the AAI exception
320          */
321         public List<ResultSet> queryByModel(String transId, String fromAppId,
322                                         String modelVersionId,
323                                         String modelInvariantId,
324                                         String modelName,
325                                         String topNodeType,
326                                         List<Map<String,Object>> startNodeFilterArrayOfHashes,
327                                         String apiVer )
328                                         throws AAIException {
329         
330                 final String transId_f = transId;
331                 final String fromAppId_f = fromAppId;
332                 final String modelVersionId_f = modelVersionId;
333                 final String modelInvId_f = modelInvariantId;
334                 final String modelName_f = modelName;
335                 final String topNodeType_f = topNodeType;
336                 final List<Map<String,Object>> startNodeFilterArrayOfHashes_f = startNodeFilterArrayOfHashes; 
337                 final String apiVer_f = apiVer; 
338                 
339                 // Find out what our time-limit should be
340                 int timeLimitSec = 0;
341                 String timeLimitString = AAIConfig.get("aai.model.query.timeout.sec");
342                 if( timeLimitString != null && !timeLimitString.equals("") ){
343                         try {
344                                 timeLimitSec = Integer.parseInt(timeLimitString);
345                         }
346                         catch ( Exception nfe ){
347                                 // Don't worry, we will leave the limit as zero - which tells us not to use it.
348                         }
349                 }
350                 
351                 if( timeLimitSec <= 0 ){
352                         // We will NOT be using a timer
353                         return queryByModel_Timed( transId, fromAppId,
354                                         modelVersionId, 
355                                         modelInvariantId,
356                                         modelName,
357                                         topNodeType,
358                                         startNodeFilterArrayOfHashes, 
359                                         apiVer );
360                 }
361                 
362                 List<ResultSet> resultList = new ArrayList<>();
363                 TimeLimiter limiter = new SimpleTimeLimiter();
364                 try {
365                         resultList = limiter.callWithTimeout(new Callable <List<ResultSet>>() {
366                             public List<ResultSet> call() throws AAIException {
367                               return queryByModel_Timed( transId_f, fromAppId_f,
368                                                 modelVersionId_f, 
369                                                 modelInvId_f,
370                                                 modelName_f,
371                                                 topNodeType_f,
372                                                 startNodeFilterArrayOfHashes_f, 
373                                                 apiVer_f );
374                             }
375                           }, timeLimitSec, TimeUnit.SECONDS, true);
376                 } 
377                 catch (AAIException ae) {
378                         // Re-throw AAIException so we get can tell what happened internally
379                         throw ae;
380                 }
381                 catch (UncheckedTimeoutException ute) {
382                         throw new AAIException("AAI_6140", "Query Processing Limit exceeded. (limit = " + timeLimitSec + " seconds)");
383                 }
384                 catch (Exception e) {
385                         throw new AAIException("AAI_6128", "Unexpected exception in queryByModel(): " + e.getMessage() );
386                 }
387
388                 return resultList;
389         }
390         
391                 
392         /**
393          * Query by model (model-ver) timed.
394          *
395          * @param transId the trans id
396          * @param fromAppId the from app id
397          * @param modelVersionId the model-version-id (unique id in model-ver)
398          * @param modelInvariantId the model-invariant-id (unique id in model)
399          * @param modelName the model name
400          * @param topNodeType the top node type
401          * @param startNodeFilterArrayOfHashes the start node filter array of hashes
402          * @param apiVer the api ver
403          * @return the array list
404          * @throws AAIException the AAI exception
405          */
406         public List<ResultSet> queryByModel_Timed(String transId, String fromAppId,
407                                               String modelVersionId,
408                                               String modelInvariantId,
409                                               String modelName,
410                                               String topNodeType,
411                                               List<Map<String,Object>> startNodeFilterArrayOfHashesVal,
412                                               String apiVer )
413                                         throws AAIException {
414                                         
415                 List<ResultSet> resultArray = new ArrayList<>();
416                 
417                 // NOTE: this method can be used for different styles of queries:
418                 //   a) They could pass neither a modelVersionId or a modelInvariantId but just pass a set of data defining start-nodes.
419                 //      Note - with no model info, we need them to pass the startNodeType for us to be able to use the
420                 //      start-node-filter data.  We would look at each start node and ensure that each has persona-model info.  
421                 //      Then use whatever model corresponds to each instance to pull that instance's data.
422                 //   b) They could pass a modelInvariantId, but no modelVersionId and no startNode info.   In this case, we
423                 //      Would look in the database for all nodes that have a model-invariant-id-local that matches what was 
424                 //      passed, and then for each of those instances, pull the data based on the corresponding model.
425                 //   c) They could pass a model-version-id, but no startNode info. We'd make sure that if a 
426                 //      model-invariant-id was also passed, that it does not conflict - but it really should be null if they
427                 //      are passing a full model-version-id.   Like case -b-, we'd do a query for all nodes
428                 //      that have persona info that corresponds to the model-version-id passed and then 
429                 //      collect data for each one.
430                 //   d) They could pass either modelVersionId or modelInvariantId AND startNodeFilter info.  In this case we
431                 //      would look at the model info to figure out what the top-node-type is, then look at the 
432                 //      top-node instances based on the startNodeFilter.   We'd only collect data for each instance if
433                 //      it's persona model info matches what was passed in.
434                 
435                 
436                 // Sorry to do this, but code that gets called with an empty hash as the first array element was causing errors
437                 List<Map<String,Object>> startNodeFilterArrayOfHashes = new ArrayList <Map<String,Object>>();
438                 if( !startNodeFilterArrayOfHashesVal.isEmpty() ){
439                         Map<String,Object> tmpH = startNodeFilterArrayOfHashesVal.get(0);
440                         if( !tmpH.isEmpty() ){
441                                 for( int i=0; i < startNodeFilterArrayOfHashesVal.size(); i++ ){
442                                         startNodeFilterArrayOfHashes.add( startNodeFilterArrayOfHashesVal.get(i) );
443                                 }
444                         }
445                 }
446                                 
447                 // ----------------------------------------------------------------------------------------------------------
448                 // Get a Hash of all the start-nodes (top instance-data node for a model-ver where we will 
449                 // start collecting data) for startNode2ModelVerHash:  
450                 //                      key = vertex-id for the startNode, 
451                 //                      value = model-version-id for the corresponding model-ver   
452                 // ----------------------------------------------------------------------------------------------------------
453                 Map<String, String> startNode2ModelVerHash = getStartNodesAndModVersionIds( transId, fromAppId,
454                                 modelVersionId, modelInvariantId, modelName, topNodeType,
455                                 startNodeFilterArrayOfHashes, apiVer ); 
456                 
457                 //System.out.println("\nDEBUG -- Here's a dump of the startnodes/model-vers: " + startNode2ModelVerHash.toString()); 
458                 
459                 // --------------------------------------------------------------------------------------------------------
460                 // Figure out what-all models (model-ver nodes) we will be dealing with 
461                 // Note - Instances must all use the same type of start-node, but do not have to all use the same model-ver.
462                 // --------------------------------------------------------------------------------------------------------
463                 Map<String, Vertex> distinctModelVersHash = new HashMap<>();
464                         // For distinctModelVersHash:  key = modelVersionId, val= modelVerVertex
465                 String startNodeType = "";
466                 if( topNodeType != null && !topNodeType.equals("") ){
467                         startNodeType = topNodeType;
468                 }
469
470                 List<String> skipModelVerIdList = new ArrayList<>();
471                 List<String> skipStartVertVerIdList = new ArrayList<>();
472                 Set <String> snKeySet = startNode2ModelVerHash.keySet();
473                 Iterator<String> startNodeIterator = snKeySet.iterator();
474                 while( startNodeIterator.hasNext() ){
475                         String modVerIdKey = (String) startNodeIterator.next();  
476                         String modVerId = startNode2ModelVerHash.get(modVerIdKey);
477                         if( !distinctModelVersHash.containsKey(modVerId) ){
478                                 // First time seeing this model-version-id
479                                 Vertex modVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver",
480                                                 "model-version-id", modVerId);
481                                 String tmpNodeType = "";
482                                 try {
483                                         tmpNodeType = getModelVerTopWidgetType( modVerVtx, "" );
484                                 }
485                                 catch( AAIException ae ){
486                                         // There must be some old bad data in the db - we will skip over this model-ver since its
487                                         // model is not good anymore - but will log that this is happening.
488                                         skipModelVerIdList.add(modVerId);
489                                         skipStartVertVerIdList.add(modVerIdKey);
490                                         System.out.println(">>>  WARNING - will not collect model data for this vertex since " +
491                                                         "it uses an inconsistant model-ver model.  Model-version-id = " + modVerId );
492                                 }
493                                 
494                                 if( tmpNodeType != null && !tmpNodeType.equals("") ){
495                                         if( startNodeType.equals("") ){
496                                                 startNodeType = tmpNodeType;
497                                         }
498                                         else if( !startNodeType.equals(tmpNodeType) ){
499                                                 String msg = "Conflict between startNode types for models involved: [" + startNodeType
500                                                                 + "], [" + tmpNodeType + "]";
501                                                 throw new AAIException("AAI_6125", msg);
502                                         }
503                                         distinctModelVersHash.put(modVerId, modVerVtx);
504                                 }
505                         }
506                 }
507                 
508                 //System.out.println("\nDEBUG -- Here's a dump of the DISTINCT model-ver hash: " + distinctModelVersHash.toString() );
509         
510                 // ------------------------------------------------------------------------------------------------------
511                 // Get the "valid-next-step" hash for each distinct model-ver
512                 // While we're at it, get a mapping of model-invariant-id|model-version to model-version-id for 
513                 //     the model-vers being used
514                 // ------------------------------------------------------------------------------------------------------
515                 Map<String, Multimap<String, String>> validNextStepHash = new HashMap<>();
516                         // validNextStepHash:   key = modelVerId, value = nextStepMap
517                 Set <String> keySet = distinctModelVersHash.keySet();
518                 Iterator<String> modelVerIterator = keySet.iterator();
519                 while( modelVerIterator.hasNext() ){
520                         String modVerKey = (String) modelVerIterator.next();
521                         if( ! skipModelVerIdList.contains(modVerKey) ){
522                                 Vertex modelVerVtx = (Vertex)distinctModelVersHash.get(modVerKey);
523                                 Multimap<String, String> tmpTopoMap = genTopoMap4ModelVer( transId, fromAppId,
524                                                 modelVerVtx, modVerKey);
525                                 validNextStepHash.put(modVerKey, tmpTopoMap);
526                         }
527                 } 
528                                 
529                 // -------------------------------------------------------------------------------------------------
530                 // Figure out what the "start-node" for each instance will be (plus the info we will use to 
531                 //       represent that in our topology)
532                 // -------------------------------------------------------------------------------------------------
533                 List<String> failedPersonaCheckVids = new ArrayList<>();
534                 Map<String, String> firstStepInfoHash = new HashMap<>(); 
535                         // For firstStepInfoHash:   key = startNodeVtxId, val=topNodeType plus personaData if applicable
536                         //                            ie. the value is what we'd use as the "first-step" for this model.
537                 if( !nodeTypeSupportsPersona( startNodeType) ){
538                         // This node type doesn't have persona info, so we just use startNodeType for the first-step-info 
539                         snKeySet = startNode2ModelVerHash.keySet();
540                         startNodeIterator = snKeySet.iterator();
541                         while( startNodeIterator.hasNext() ){
542                                 String vtxKey = (String) startNodeIterator.next();
543                                 firstStepInfoHash.put(vtxKey,startNodeType);
544                         }
545                 }
546                 else { 
547                         // Need to check that this node's persona data is good and if it is - use it for the first step info
548                         snKeySet = startNode2ModelVerHash.keySet();
549                         startNodeIterator = snKeySet.iterator();
550                         while( startNodeIterator.hasNext() ){
551                                 String vtxKey = (String) startNodeIterator.next();
552                                 Iterator<Vertex> vtxIterator = this.engine.asAdmin().getReadOnlyTraversalSource().V(vtxKey);
553                                 Vertex tmpVtx = (Vertex)vtxIterator.next();
554                                 String thisVtxModelVerId = startNode2ModelVerHash.get(vtxKey);
555                                 if( skipModelVerIdList.contains(thisVtxModelVerId) ){
556                                         // Skip this vertex because it uses a model-ver that is bad
557                                         continue;
558                                 }
559                                 Vertex modelVerVtx = (Vertex)distinctModelVersHash.get(thisVtxModelVerId);
560                                 Vertex modelVtx = getModelGivenModelVer( modelVerVtx, "" );
561                                 String modInvId = modelVtx.<String>property("model-invariant-id").orElse(null);
562                                 String personaModInvId = tmpVtx.<String>property(addDBAliasedSuffix("model-invariant-id")).orElse(null);
563                                 String personaModVerId = tmpVtx.<String>property(addDBAliasedSuffix("model-version-id")).orElse(null);
564                                 if( modInvId.equals(personaModInvId) && thisVtxModelVerId.equals(personaModVerId) ){
565                                         String tmpPersonaInfoStr = startNodeType + "," + personaModInvId + "," + personaModVerId;
566                                         firstStepInfoHash.put(vtxKey, tmpPersonaInfoStr );
567                                 }
568                                 else { 
569                                         // we won't use this start node below when we collect data because it should have
570                                         // had persona data that matched it's model - but it did not.
571                                         failedPersonaCheckVids.add(vtxKey);
572                                 }
573                         }       
574                 }       
575
576                 //System.out.println("\nDEBUG -- Here's a dump of the firstStepInfoHash hash: " + firstStepInfoHash.toString() );
577                 
578                 // ------------------------------------------------------------------------------------------------
579                 // Loop through each start-node, collect it's data using collectInstanceData() and put the 
580                 //      resultSet onto the resultArray.
581                 // ------------------------------------------------------------------------------------------------
582                 
583                 // Make sure they're not bringing back too much data
584                 String maxString = AAIConfig.get("aai.model.query.resultset.maxcount");
585                 if( maxString != null &&  !maxString.equals("") ){
586                         int maxSets = 0;
587                         try {
588                                 maxSets = Integer.parseInt(maxString);
589                         }
590                         catch ( Exception nfe ){
591                                 // Don't worry, we will leave the max as zero - which tells us not to use it.
592                         }
593                         
594                         if( maxSets > 0 && (startNode2ModelVerHash.size() > maxSets) ){
595                                 String msg = " Query returns " + startNode2ModelVerHash.size() + " resultSets.  Max allowed is: " + maxSets;
596                                 throw new AAIException("AAI_6141", msg);
597                         }
598                 }
599                 
600                 snKeySet = startNode2ModelVerHash.keySet();
601                 startNodeIterator = snKeySet.iterator();
602                 while( startNodeIterator.hasNext() ){
603                         String topNodeVtxId  = (String) startNodeIterator.next();
604                         if( failedPersonaCheckVids.contains(topNodeVtxId) ){
605                                 // Skip this vertex because it failed it's persona-data check above
606                                 continue;
607                         }
608                         if( skipStartVertVerIdList.contains(topNodeVtxId) ){
609                                 // Skip this vertex because it uses a model-ver that is bad
610                                 continue;
611                         }
612                         
613                         Iterator<Vertex> vtxIterator = this.engine.asAdmin().getReadOnlyTraversalSource().V(topNodeVtxId);
614                         Vertex tmpStartVtx = (Vertex)vtxIterator.next();
615                         String elementLocationTrail = firstStepInfoHash.get(topNodeVtxId); 
616                         String modelVerId = startNode2ModelVerHash.get(topNodeVtxId);
617                         Multimap<String, String> validNextStepMap = validNextStepHash.get(modelVerId);
618                         
619                         List<String> vidsTraversed = new ArrayList<>();
620                         Map<String,String> emptyDelKeyHash = new HashMap<>();
621                         Map<String,String> emptyNQElementHash = new HashMap<>();  // Only applies to Named Queries
622                         ResultSet tmpResSet = collectInstanceData( transId, fromAppId,
623                                         tmpStartVtx, elementLocationTrail, 
624                                         validNextStepMap, vidsTraversed, 0, emptyDelKeyHash, emptyNQElementHash, apiVer );
625                         
626                         resultArray.add(tmpResSet);
627                 }
628                 
629                 return resultArray;
630                 
631         }// queryByModel_Timed()
632         
633                         
634         
635         /**
636          * Run delete by model-ver.
637          *
638          * @param transId the trans id
639          * @param fromAppId the from app id
640          * @param modelVersionId the model version id -- unique id for a model-ver node
641          * @param topNodeTypeVal the top node type val -- required if no model-version-id is passed
642          * @param startNodeFilterHash the start node filter hash -- used to locate the first node of instance data
643          * @param apiVer the api ver
644          * @param resVersion the res version -- resourceVersion of the top/first widget in the model instance
645          * @return HashMap (keys = vertexIds that were deleted)
646          * @throws AAIException the AAI exception
647          */
648         public Map<String,String> runDeleteByModel( String transId, String fromAppId,
649                         String modelVersionId, String topNodeTypeVal, Map<String,Object> startNodeFilterHash, String apiVer, String resVersion ) 
650                                         throws AAIException {
651                 
652                 Map<String,String> retHash = new HashMap<>();
653                                 
654                 // Locate the Model-ver node to be used 
655                 Vertex modelVerVtx = null;
656                 if( modelVersionId != null && !modelVersionId.equals("") ){
657                         modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", 
658                                         "model-version-id", modelVersionId);
659                 }
660                 else {
661                         // if they didn't pass the modelVersionId, then we need to use the startNode to figure it out
662                         // Locate the starting node based on the start node params
663                         if( topNodeTypeVal == null || topNodeTypeVal.equals("") ){
664                                 throw new AAIException("AAI_6118", "If no model info is passed, then topNodeType is required. ");
665                         }
666                         
667                         Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(topNodeTypeVal, startNodeFilterHash);
668                         if (!result.isPresent()) {
669                                 throw new AAIException("AAI_6114", "No Node of type " + topNodeTypeVal + " found for properties");
670                         }
671                         Vertex startVtx = result.get();
672                         
673                         String startVertModVerId = startVtx.<String>property(addDBAliasedSuffix("model-version-id")).orElse(null);
674                         modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver", 
675                                         "model-version-id", startVertModVerId);
676                 }
677                 
678                 if( modelVerVtx == null ){
679                         throw new AAIException("AAI_6114", "Could not determine the model-ver for the given input parameters. ");
680                 }
681
682                 String topNType = "unknown";
683                 String modelType = getModelTypeFromModelVer( modelVerVtx, "" );
684                 
685                 if( modelType.equals("widget") ){
686                         // If they want to delete using a widget-level model..  That is just a delete of the one 
687                         //      instance of one of our nodes.  
688                         String widgModNodeType = modelVerVtx.<String>property("model-name").orElse(null);
689                         if( (widgModNodeType == null) || widgModNodeType.equals("") ){
690                                 String msg = "Could not find model-name for the widget model  [" + modelVersionId + "].";
691                                 throw new AAIException("AAI_6132", msg);
692                         }
693                         Optional<Vertex> result = dbMethHelper.locateUniqueVertex(widgModNodeType, startNodeFilterHash);
694                         if (!result.isPresent()) {
695                                 throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties");
696                         }
697                         Vertex widgetVtx = result.get();
698                         String widgId = widgetVtx.id().toString();
699                         serializer.delete(widgetVtx, resVersion, true);
700                         retHash.put(widgId, widgModNodeType);
701                         return retHash;
702                 }
703                 
704                 // ---------------------------------------------------------------------------------
705                 // If we got to here, this must be either a service or resource model.
706                 // So, we'll need to get a Hash of which parts of the model to delete.
707                 //  NOTE- deleteByModel is deleting data based on one specific version of a model.  
708                 // ---------------------------------------------------------------------------------
709                 String chkFirstNodePersonaModInvId = "";
710                 String chkFirstNodePersonaModVerId = "";
711                 String personaData = "";
712                 Vertex firstModElementVertex = getTopElementForSvcOrResModelVer( modelVerVtx, "" );
713                 topNType = getModElementWidgetType( firstModElementVertex, "" );
714                 if( (topNType == null) || topNType.equals("") ){
715                         String msg = "Could not determine the top-node nodeType for model-version-id: [" + modelVersionId + "]";
716                         throw new AAIException("AAI_6132", msg);
717                 }
718                 if( nodeTypeSupportsPersona(topNType) ){
719                         Vertex modelVtx = getModelGivenModelVer(modelVerVtx,"");
720                         chkFirstNodePersonaModInvId = modelVtx.<String>property("model-invariant-id").orElse(null);
721                         chkFirstNodePersonaModVerId = modelVerVtx.<String>property("model-version-id").orElse(null);
722                         personaData = "," + chkFirstNodePersonaModInvId + "," + chkFirstNodePersonaModVerId;
723                 }
724                 
725                 // Get the deleteKeyHash for this model
726                 String incomingTrail = "";
727                 Map<String, String> currentHash = new HashMap<>();
728                 Map<String, Vertex> modConHash = new HashMap<>();
729                 ArrayList <String>  vidsTraversed = new ArrayList<>();
730                 Map<String, String> delKeyHash = collectDeleteKeyHash( transId, fromAppId,
731                                   firstModElementVertex, incomingTrail, currentHash, vidsTraversed, 
732                                   0, modConHash, 
733                                   chkFirstNodePersonaModInvId, chkFirstNodePersonaModVerId ); 
734         
735                 
736                 System.out.println("\n ----DEBUG -----:  Delete Hash for model: [" + modelVersionId + "] looks like: ");
737                 for( Map.Entry<String, String> entry : delKeyHash.entrySet() ){
738                         System.out.println("key = [" + entry.getKey() + "], val = [" + entry.getValue() + "]");
739                 }
740                 System.out.println("\n -----");
741                 // Locate the starting node that we'll use to start looking for instance data
742                 Optional<Vertex> result = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterHash);
743                 if (!result.isPresent()) {
744                         throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties");
745                 }
746                 Vertex startVtx = result.get();
747                 if( !chkFirstNodePersonaModInvId.equals("") ){
748                         // NOTE:  For Service or Resource models, if this is a nodeType that supports persona's, then
749                         //              we need to make sure that the start node matches the persona values.
750                         String startVertPersonaModInvId = startVtx.<String>property(addDBAliasedSuffix("model-invariant-id")).orElse(null);
751                         String startVertPersonaModVerId = startVtx.<String>property(addDBAliasedSuffix("model-version-id")).orElse(null);
752                         if( !chkFirstNodePersonaModInvId.equals(startVertPersonaModInvId) 
753                                         || !chkFirstNodePersonaModVerId.equals(startVertPersonaModVerId) ){
754                                 String msg = "Persona-Model data mismatch for start node (" + topNType +  "), " +
755                                                 startNodeFilterHash ;
756                                 throw new AAIException("AAI_6114", msg);
757                         }
758                 }
759                 String topVid = startVtx.id().toString();
760                 
761                 // Read the model-ver into a Map for processing
762                 Multimap<String, String> validNextStepMap = genTopoMap4ModelVer(transId, fromAppId,
763                                 modelVerVtx, modelVersionId);
764                         
765                 // Collect the data
766                 String elementLocationTrail = topNType + personaData;
767                 vidsTraversed = new ArrayList<>();
768                 Map<String,String> emptyHash = new HashMap<>();  
769                 
770                 // Pass emptyHash for the NQElement hash since that parameter only applies to Named Queries
771                 ResultSet retResSet = collectInstanceData( transId, fromAppId,
772                                 startVtx, elementLocationTrail, 
773                                 validNextStepMap, vidsTraversed, 0, delKeyHash, emptyHash, apiVer );
774                 
775                 // Note: the new ResultSet will have each element tagged with the del flag so we'll know if it
776                 //              should be deleted or not - so loop through the results in a try-block since some things 
777                 //              will get auto-deleted by parents before we get to them --- and try to remove each one.
778                 String vidToResCheck = topVid;
779                 
780                 retHash = deleteAsNeededFromResultSet( transId, fromAppId, retResSet, 
781                                 vidToResCheck, apiVer, resVersion, emptyHash );
782                 //String msgStr = "processed deletes for these vids: (\n"+ retHash.keySet().toString() + ").";
783                 
784                 return retHash;
785                   
786         }// End of runDeleteByModel()
787
788                                 
789                                 
790         /**
791          * Delete as needed from result set.
792          *
793          * @param transId the trans id
794          * @param fromAppId the from app id
795          * @param resSet the res set
796          * @param vidToResCheck -- this vertex will need to have its resource-version checked
797          * @param apiVer the api ver
798          * @param resVersion the res version
799          * @param hashSoFar the hash so far -- hash of what's been deleted so far
800          * @return String
801          * @throws AAIException the AAI exception
802          */
803         public Map<String,String> deleteAsNeededFromResultSet(String transId, String fromAppId,
804                                                           ResultSet resSet, String vidToResCheck, String apiVer, String resVersion, Map<String,String> hashSoFar )
805                                         throws AAIException
806         {
807                 Map<String,String> retHash = new HashMap<>();
808                 retHash.putAll( hashSoFar );
809                 Boolean deleteIt = false;
810                         
811                 if( resSet.getVert() == null ){
812                         return retHash;
813                 }
814                 
815                 Vertex thisVtx = resSet.getVert();
816                 String thisGuyId = "";
817                 String thisNT = "";
818                 String thisGuyStr = "";
819                 
820                 Boolean gotVtxOK = false;
821                 try {
822                         if( thisVtx != null ){
823                                 thisGuyId = thisVtx.id().toString();
824                                 thisNT = thisVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
825                                 thisGuyStr = thisGuyId + "[" + thisNT + " found at:" + resSet.getLocationInModelSubGraph() + "]";
826                                 
827                                 // NOTE -- will try to set the NodeType to itself to see if the node has been deleted already in 
828                                 //       this transaction. It lets you get properties from nodes being deleted where the 
829                                 //       delete hasn't been committed yet.  This check used to be accomplished with a call to 
830                                 //       "vtx.isRemoved()" but that was a Titan-only feature and is not available anymore since
831                                 //       we no longer use Titan vertices.
832                                 // If we don't do this check, we get errors later when we try to delete the node.
833                                 thisVtx.property(AAIProperties.NODE_TYPE, thisNT);
834                                 gotVtxOK = true;
835                         }
836                 }
837                 catch (Exception ex) {
838                         // Sometimes things have already been deleted by the time we get to them - just log it.
839                         LOGGER.warn("Exception when trying to delete: " + thisGuyStr + ".  msg = " + ex.getMessage(), ex);
840                 }
841                 
842                 if( !gotVtxOK ){
843                         // The vertex must have already been removed.   Just return.
844                         // Note - We need to catch this because the DB sometimes can still have the vtx 
845                         // and be able to get its ID but it is flagged internally as removed already.
846                         return retHash;
847                 }
848                 else {
849                         if( resSet.getNewDataDelFlag() != null && resSet.getNewDataDelFlag().equals("T") ){
850                                 LOGGER.info(">>  will try to delete this one >> " + thisGuyStr);
851                                 
852                                 try {
853                                         Boolean requireResourceVersion = false;
854                                         if( thisGuyId.equals(vidToResCheck) ){
855                                                 // This is the one vertex that we want to check the resourceId before deleting
856                                                 requireResourceVersion = true;
857                                         }
858                                         this.serializer.delete(thisVtx, resVersion, requireResourceVersion);
859                                 }
860                                 catch (AAIException ae) {
861                                         String errorCode = ae.getErrorObject().getErrorCode();
862                                         if (  errorCode.equals("6130") ||  errorCode.equals("6131") ) {
863                                                 // They didn't pass the correct resource-version for the top node.
864                                                 throw ae;
865                                         }
866                                         else {
867                                                 String errText = ae.getErrorObject().getErrorText();
868                                                 String errDetail = ae.getMessage();
869                                                 LOGGER.warn("Exception when deleting " + thisGuyStr + ".  ErrorCode = " + errorCode + 
870                                                                 ", errorText = " + errText + ", details = " + errDetail);
871                                         }
872                                 }
873                                 catch( Exception e ){
874                                         // We'd expect to get a "node not found" here sometimes depending on the order that 
875                                         // the model has us finding / deleting nodes.
876                                         // Ignore the exception - but log it so we can see what happened.
877                                         LOGGER.warn("Exception when deleting " + thisGuyStr + e.getMessage(), e);
878                                 }
879                                 
880                                 // We can't depend on a thrown exception to tell us if a node was deleted since it may
881                                 // have been auto=deleted before this removeAaiNode() call.  
882                                 // --- Not sure if we would want to check anything here -- because the graph.commit() is done outside of this call.
883                                 
884                                 deleteIt = true;
885                         }
886                         else {
887                                 // --- DEBUG ---- 
888                                 System.out.println(">>>>>>> NOT DELETING THIS ONE >>>> " + thisGuyStr );
889                                 List<String> retArr = dbMethHelper.getVertexProperties(thisVtx);
890                                 for( String info : retArr ){ System.out.println(info); }
891                                 // --- DEBUG ----
892                         }
893                 }
894                 
895                 // Now call this routine for the sub-resultSets
896                 List <ResultSet> subResultSetList = resSet.getSubResultSet();
897                 Iterator <ResultSet> subResSetIter = subResultSetList.iterator();
898                 while( subResSetIter.hasNext() ){
899                         ResultSet tmpSubResSet = subResSetIter.next();
900                         retHash = deleteAsNeededFromResultSet( transId, fromAppId, tmpSubResSet, 
901                                         vidToResCheck, apiVer, resVersion, retHash );
902                 }
903                 
904                 if( deleteIt ){
905                         retHash.put(thisGuyId, thisGuyStr);
906                 }
907                 
908                 return retHash;
909                                 
910         }// deleteAsNeededFromResultSet()
911                 
912         
913
914         /**
915          * Query by named query (old version).
916          *
917          * @param transId the trans id
918          * @param fromAppId the from app id
919          * @param namedQueryUuid the named query uuid
920          * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data
921          * @param apiVer the api ver
922          * @return resultSet
923          * @throws AAIException the AAI exception
924          */
925         public List<ResultSet> queryByNamedQuery(String transId, String fromAppId,
926                                              String namedQueryUuid,
927                                              ArrayList <Map<String,Object>> startNodeFilterArrayOfHashes,
928                                              String apiVer )
929                                         throws AAIException {
930         
931                 String dummyCutPoint = null;
932                 Map<String,Object> dummySecondaryFilterHash = null;
933                 
934                 return queryByNamedQuery( transId, fromAppId,
935                                 namedQueryUuid,  
936                                 startNodeFilterArrayOfHashes, 
937                                 apiVer,
938                                 dummyCutPoint,
939                                 dummySecondaryFilterHash ); 
940         }
941         
942
943         /**
944          * Query by named query.
945          *
946          * @param transId the trans id
947          * @param fromAppId the from app id
948          * @param namedQueryUuid the named query uuid
949          * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data
950          * @param apiVer the api ver
951          * @param secondaryCutPoint nodeType where we will prune if secondary filter is not met
952          * @param secondaryFilterHash secondary filter params
953          * @return resultSet
954          * @throws AAIException the AAI exception
955          */
956         public List<ResultSet> queryByNamedQuery(String transId, String fromAppId,
957                                              String namedQueryUuid,
958                                              List<Map<String,Object>> startNodeFilterArrayOfHashes,
959                                              String apiVer,
960                                              String secondaryFilterCutPoint,
961                                              Map<String,Object> secondaryFilterHash )
962                                         throws AAIException {
963         
964                 final String transId_f = transId;
965                 final String fromAppId_f = fromAppId;
966                 final String namedQueryUuid_f = namedQueryUuid;
967                 final List<Map<String,Object>> startNodeFilterArrayOfHashes_f = startNodeFilterArrayOfHashes; 
968                 final String apiVer_f = apiVer; 
969                 final String secondaryFilterCutPoint_f = secondaryFilterCutPoint; 
970                 final Map<String,Object> secondaryFilterHash_f = secondaryFilterHash;   
971                 
972                 // Find out what our time-limit should be
973                 int timeLimitSec = 0;
974                 String timeLimitString = AAIConfig.get("aai.model.query.timeout.sec");
975                 if( timeLimitString != null && !timeLimitString.equals("") ){
976                         try {
977                                 timeLimitSec = Integer.parseInt(timeLimitString);
978                         }
979                         catch ( Exception nfe ){
980                                 // Don't worry, we will leave the limit as zero - which tells us not to use it.
981                         }
982                 }
983
984                 if( timeLimitSec <= 0 ){
985                         // We will NOT be using a timer
986                         return queryByNamedQuery_Timed( transId, fromAppId,
987                                         namedQueryUuid,
988                                         startNodeFilterArrayOfHashes, 
989                                         apiVer,
990                                         secondaryFilterCutPoint_f,
991                                         secondaryFilterHash_f );
992                 }
993                 
994                 List<ResultSet> resultList = new ArrayList<>();
995                 TimeLimiter limiter = new SimpleTimeLimiter();
996                 try {
997                         resultList = limiter.callWithTimeout(new Callable <List<ResultSet>>() {
998                             public List<ResultSet> call() throws AAIException {
999                               return queryByNamedQuery_Timed( transId_f, fromAppId_f,
1000                                                         namedQueryUuid_f,
1001                                                         startNodeFilterArrayOfHashes_f, 
1002                                                         apiVer_f,
1003                                                         secondaryFilterCutPoint_f,
1004                                                         secondaryFilterHash_f );
1005                             }
1006                         }, timeLimitSec, TimeUnit.SECONDS, true);
1007                         
1008                 } 
1009                 catch (AAIException ae) {
1010                         // Re-throw AAIException so we get can tell what happened internally
1011                         throw ae;
1012                 }
1013                 catch (UncheckedTimeoutException ute) {
1014                         throw new AAIException("AAI_6140", "Query Processing Limit exceeded. (limit = " + timeLimitSec + " seconds)");
1015                 }
1016                 catch (Exception e) {
1017                         throw new AAIException("AAI_6128", "Unexpected exception in queryByNamedQuery(): " + e.getMessage() );
1018                 }
1019
1020                 return resultList;
1021         }
1022         
1023         
1024         /**
1025          * Query by named query timed.
1026          *
1027          * @param transId the trans id
1028          * @param fromAppId the from app id
1029          * @param namedQueryUuid the named query uuid
1030          * @param startNodeFilterArrayOfHashes the start node filter array of hashes --used to locate the first nodes of instance data  
1031          * @param apiVer the api ver
1032          * @param secondaryFilterCutPoint the nodeType where we will parse for the secondary Filter
1033          * @param secondaryFilterHash the secondary filter hash 
1034          * @return resultSet
1035          * @throws AAIException the AAI exception
1036          */
1037         public List<ResultSet> queryByNamedQuery_Timed(String transId, String fromAppId,
1038                                                    String namedQueryUuid,
1039                                                    List<Map<String,Object>> startNodeFilterArrayOfHashes,
1040                                                    String apiVer,
1041                                                    String secondaryFilterCutPoint,
1042                                                    Map<String,Object> secondaryFilterHash
1043                         ) 
1044                                         throws AAIException {
1045                 
1046                 // Locate the Query to be used
1047                 Vertex queryVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query",
1048                                 "named-query-uuid", namedQueryUuid);
1049                 
1050                 // Get the first/top named-query-element used by this query
1051                 Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, queryVtx, "named-query-element");
1052                 Vertex firstNqElementVert = null;
1053                 int count = 0;
1054                 String topNType = "";
1055                 while( vertI != null && vertI.hasNext() ){
1056                         firstNqElementVert = vertI.next();
1057                         count++;
1058                         topNType = getNqElementWidgetType( transId, fromAppId,  firstNqElementVert, "" );
1059                 }
1060                 
1061                 if( count < 1 ){
1062                         // A named query must start with a single top element
1063                         throw new AAIException("AAI_6133", "No top-node defined for named-query-uuid = [" + namedQueryUuid + "]");
1064                 }
1065                 else if( count > 1 ){
1066                         // A named query should start with a single top element
1067                         throw new AAIException("AAI_6133", "More than one top-node defined for named-query-uuid = [" + namedQueryUuid + "]");
1068                 }
1069                 if( (topNType == null) || topNType.equals("") ){
1070                         String msg = "Could not determine the top-node nodeType for Named Query: [" + namedQueryUuid + "]";
1071                         throw new AAIException("AAI_6133", msg);
1072                 }
1073                 
1074                 // Read the topology into a hash for processing
1075                 Multimap<String, String> validNextStepMap = genTopoMap4NamedQ(transId, fromAppId, queryVtx, namedQueryUuid);
1076
1077                 List<Vertex> startVertList = new ArrayList<>();
1078                 if( startNodeFilterArrayOfHashes.size() == 1 ){
1079                         // If there is only one set of startFilter info given, then allow it to possibly not be
1080                         // defining just one start node.
1081                         Map<String, Object> cleanHash = new HashMap<>();
1082                         Map<String, Object> tmpHash = startNodeFilterArrayOfHashes.get(0);
1083                         Set <String> propKeySet = tmpHash.keySet();
1084                         Iterator<String> propIter = propKeySet.iterator();
1085                         Introspector obj = loader.introspectorFromName(topNType);
1086                         Set<String> keys = obj.getKeys();
1087                         boolean foundIndexedField = false;
1088                         int propertiesSet = 0;
1089                         while( propIter.hasNext() ){
1090                                 String oldVtxKey = (String) propIter.next(); 
1091                                 String newKey = oldVtxKey;
1092                                 String [] parts = oldVtxKey.split("\\.");
1093                                 if( parts.length == 2 ){
1094                                         newKey = parts[1];
1095                                 }
1096                                 Object obVal = tmpHash.get(oldVtxKey);
1097                                 if (obj.hasProperty(newKey)) {
1098                                         if (keys.contains(newKey)) {
1099                                                 foundIndexedField = true;
1100                                         }
1101                                         obj.setValue(newKey, obVal);
1102                                         propertiesSet++;
1103                                 }
1104                         }
1105                         //we found all the properties in the startNodeType
1106                         if (propertiesSet == propKeySet.size()) {
1107                                 if (foundIndexedField) {
1108                                         QueryBuilder builder = this.engine.getQueryBuilder().exactMatchQuery(obj);
1109                                         startVertList = builder.toList();
1110                                 } else {
1111                                         //force a filter from aai-node-type
1112                                         QueryBuilder builder = this.engine.getQueryBuilder().createContainerQuery(obj).exactMatchQuery(obj);
1113                                         startVertList = builder.toList();
1114                                 }
1115                         } else {
1116                                 Optional<Vertex> tmpVtx = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterArrayOfHashes.get(0));
1117                                 // Only found one, so just use it.
1118                                 if (tmpVtx.isPresent()) {
1119                                         startVertList.add(tmpVtx.get());
1120                                 }
1121                         }
1122                 }
1123                 else {
1124                         // Since they give an array of startNodeFilterHash info, we expect each one
1125                         // to just point to one node.
1126                         for( int i = 0; i < startNodeFilterArrayOfHashes.size(); i++ ){
1127                                 // Locate the starting node for each set of data
1128                                 Optional<Vertex> tmpVtx = dbMethHelper.searchVertexByIdentityMap(topNType, startNodeFilterArrayOfHashes.get(i));
1129                                 if (tmpVtx.isPresent()) {
1130                                         startVertList.add(tmpVtx.get());
1131                                 }
1132                         }
1133                 }
1134                 
1135                 if (startVertList.isEmpty()) {
1136                         throw new AAIException("AAI_6114", "No Node of type " + topNType + " found for properties");
1137                 }
1138                 // Make sure they're not bringing back too much data
1139                 String maxString = AAIConfig.get("aai.model.query.resultset.maxcount");
1140                 if( maxString != null &&  !maxString.equals("") ){
1141                         int maxSets = Integer.parseInt(maxString);
1142                         if( startVertList.size() > maxSets ){
1143                                 String msg = " Query returns " + startVertList.size() + " resultSets.  Max allowed is: " + maxSets;
1144                                 throw new AAIException("AAI_6141", msg);
1145                         }
1146                 }
1147                 
1148                 // Loop through each start node and get its data
1149                 List<ResultSet> resSetList = new ArrayList<>();
1150                 for( int i = 0; i < startVertList.size(); i++ ){
1151                         Vertex startVtx = startVertList.get(i);
1152                         // Collect the data
1153                         String elementLocationTrail = topNType;
1154                         ArrayList <String>  vidsTraversed = new ArrayList<>();
1155                         Map<String,String> emptyDelKeyHash = new HashMap<>();  // Does not apply to Named Queries
1156                                         
1157                         // Get the mapping of namedQuery elements to our widget topology for this namedQuery
1158                         String incomingTrail = "";
1159                         Map<String, String> currentHash = new HashMap<>();
1160                         
1161                         Map<String,String> namedQueryElementHash = collectNQElementHash( transId, fromAppId,
1162                                           firstNqElementVert, incomingTrail, currentHash, vidsTraversed, 0 );
1163                         
1164                         vidsTraversed = new ArrayList<>();
1165                         ResultSet tmpResSet = collectInstanceData( transId, fromAppId,
1166                                         startVtx, elementLocationTrail, 
1167                                         validNextStepMap, vidsTraversed, 0, emptyDelKeyHash, namedQueryElementHash, apiVer );
1168                         resSetList.add(tmpResSet);
1169                 }
1170                 
1171                 // If a secondary filter was defined, we will prune the collected instance data result set(s) based on it.
1172                 List<ResultSet> prunedResSetList = new ArrayList<>();
1173                 if( resSetList != null && !resSetList.isEmpty() ){
1174                         for( int i = 0; i < resSetList.size(); i++ ){
1175                                 if( secondaryFilterCutPoint == null || secondaryFilterCutPoint.equals("") || secondaryFilterHash == null ){
1176                                         // They didn't want to do any pruning, so just use the results we already had
1177                                         prunedResSetList.add(resSetList.get(i));
1178                                 }
1179                                 else {
1180                                         ResultSet tmpResSet = pruneResultSet(resSetList.get(i), secondaryFilterCutPoint, secondaryFilterHash);
1181                                         if( tmpResSet != null ){
1182                                                 prunedResSetList.add(tmpResSet);
1183                                         }
1184                                 }
1185                         }
1186                 }
1187                 
1188                 // Since a NamedQuery can mark some nodes as "do-not-display", we need to collapse our resultSet so 
1189                 // does not display those nodes.
1190                 List<ResultSet> collapsedResSetList = new ArrayList<>();
1191                 if( prunedResSetList != null && !prunedResSetList.isEmpty() ){
1192                         for( int i = 0; i < prunedResSetList.size(); i++ ){
1193                                 // Note - a single resultSet could be collapsed into many smaller ones if they
1194                                 //    marked all the "top" node-elements as do-not-output.   Ie. the query may
1195                                 //    have had a top-node of "generic-vnf" which joins down to different l-interfaces.
1196                                 //    If they only want to see the l-interfaces, then a single result set
1197                                 //    would be "collapsed" into many separate resultSets - each of which is 
1198                                 //    just a single l-interface.
1199                                 List<ResultSet> tmpResSetList = collapseForDoNotOutput(prunedResSetList.get(i));
1200                                 if( tmpResSetList != null && !tmpResSetList.isEmpty() ){
1201                                         for( int x = 0; x < tmpResSetList.size(); x++ ){
1202                                                 //showResultSet( tmpResSetList.get(x), 0 ); //DEBUG-- this was just for testing
1203                                                 collapsedResSetList.add(tmpResSetList.get(x));
1204                                         }
1205                                 }
1206                         }
1207                 }
1208                 
1209                 return collapsedResSetList;
1210                 
1211         }// End of queryByNamedQuery()
1212
1213         
1214         /**
1215          * Prune a result set as per a secondary filter.
1216          *
1217          * @param resSetVal the res set val
1218          * @param cutPoint the nodeType where the trim will happen
1219          * @param secFilterHash hash of properties and values to use as the secondary filter
1220          * @return pruned result set
1221          * @throws AAIException the AAI exception
1222          */
1223         public ResultSet pruneResultSet(ResultSet resSetVal, String cutPointType, Map<String,Object> secFilterHash )
1224                 throws AAIException {
1225                 
1226                 // Given a ResultSet and some secondary filter info, do pruning as needed 
1227                 ResultSet pResSet = new ResultSet();
1228                                 
1229                 // For this ResultSet, we will see if we are on a node of the type that is our cutPoint; 
1230                 //   then only keep it if we peek "below" and see a match for our filter.
1231                                 
1232                 String nt = resSetVal.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null);
1233                 if( nt != null && nt.equals(cutPointType) ){
1234                         // We are on the type of node that may need to be "pruned" along with it's sub-results
1235                         if( ! satisfiesFilters(resSetVal, secFilterHash) ){
1236                                 // Return an empty result set since we are pruning at this level.
1237                                 return pResSet;
1238                         }
1239                 }
1240                 
1241                 // If we made it to here, we will not be pruning at this level, so we will 
1242                 // be returning a copy of this resultSet that has it's subResults pruned (as needed).
1243                 pResSet.setVert(resSetVal.getVert());
1244                 pResSet.setDoNotOutputFlag(resSetVal.getDoNotOutputFlag());
1245                 pResSet.setExtraPropertyHash(resSetVal.getExtraPropertyHash());
1246                 pResSet.setLocationInModelSubGraph(resSetVal.getLocationInModelSubGraph());
1247                 pResSet.setNewDataDelFlag(resSetVal.getNewDataDelFlag());
1248                 pResSet.setPropertyLimitDesc(resSetVal.getPropertyLimitDesc());
1249                 pResSet.setPropertyOverRideHash(resSetVal.getPropertyOverRideHash());
1250                                 
1251                 if( !resSetVal.getSubResultSet().isEmpty() ){
1252                         ListIterator<ResultSet> listItr = resSetVal.getSubResultSet().listIterator();
1253                         List<ResultSet> newSubSetList = new ArrayList<>();
1254                         while( listItr.hasNext() ){
1255                                 ResultSet tmpSubResSet = pruneResultSet( listItr.next(), cutPointType, secFilterHash );
1256                                 if( tmpSubResSet.getVert() != null ){
1257                                         // This one wasn't pruned - so keep it.
1258                                         newSubSetList.add(tmpSubResSet);
1259                                 }
1260                         }
1261                         pResSet.setSubResultSet(newSubSetList);
1262                 }   
1263                 
1264                 return pResSet;
1265                 
1266         }// End pruneResultSet()
1267         
1268         
1269         /**
1270          * Satisfies hash of filters.
1271          *
1272          * @param resSet the res set
1273          * @param filterHash the filter hash
1274          * @return true, if successful
1275          * @throws AAIException the AAI exception
1276          */
1277         public boolean satisfiesFilters(ResultSet resSet, Map<String,Object> filterHash )
1278                         throws AAIException {
1279                   
1280                   if( filterHash.isEmpty() ){
1281                           // Nothing to look for, so no, we didn't find it.
1282                           return false;
1283                   }
1284                   
1285                   Iterator <?> it = filterHash.entrySet().iterator();
1286                   while( it.hasNext() ){
1287                           Map.Entry<?,?> filtEntry = (Map.Entry<?,?>) it.next();
1288                           String propNodeTypeDotName = (filtEntry.getKey()).toString();
1289                           String fpv = (filtEntry.getValue()).toString();
1290                           
1291                           int periodLoc = propNodeTypeDotName.indexOf(".");
1292                           if( periodLoc <= 0 ){
1293                                   String emsg = "Bad filter param key passed in: [" + propNodeTypeDotName + "].  Expected format = [nodeName.paramName]\n";
1294                                   throw new AAIException("AAI_6120", emsg);
1295                           }
1296                           else {
1297                                   String fnt = propNodeTypeDotName.substring(0,periodLoc);
1298                                   String fpn = propNodeTypeDotName.substring(periodLoc + 1);
1299                                   if( filterMetByThisSet( resSet, fnt, fpn, fpv ) ){
1300                                           //System.out.println(" DEBUG -- satisfied/matched filter: [" + fnt + "|" + fpn + "|" + fpv + "].");
1301                                   }
1302                                   else {
1303                                           //System.out.println(" DEBUG -- NOT satisfied/matched filter: [" + fnt + "|" + fpn + "|" + fpv + "].");
1304                                           return false;
1305                                   }
1306                           }
1307                   }
1308                   
1309                   // Made it through all the filters -- it found what we were looking for.
1310                   return true;
1311                   
1312           }// end of satisfiesFilters()
1313         
1314         
1315         /**
1316          * Filter met by this set.
1317          *
1318          * @param resSet the res set
1319          * @param filtNodeType the filt node type
1320          * @param filtPropName the filt prop name
1321          * @param filtPropVal the filt prop val
1322          * @return true, if successful
1323          */
1324         public boolean filterMetByThisSet(ResultSet resSet, String filtNodeType, String filtPropName, String filtPropVal ) {
1325         // Note - we are just looking for a positive match for one filter for this resultSet
1326                 // NOTE: we're expecting the filter to have a format like this: "nodeType.parameterName:parameterValue"
1327                 
1328                   Vertex vert = resSet.getVert();
1329                   if( vert == null ){
1330                           return false;
1331                   }
1332                   else {
1333                           String nt = resSet.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null);
1334                           if( nt.equals( filtNodeType ) ){
1335                                   if( filtPropName.equals("vertex-id") ){
1336                                           // vertex-id can't be gotten the same way as other properties
1337                                           String thisVtxId = vert.id().toString();
1338                                           if( thisVtxId.equals(filtPropVal) ){
1339                                                   return true;
1340                                           }
1341                                   }
1342                                   else {
1343                                           Object thisValObj = vert.property(filtPropName).orElse(null);
1344                                           if( thisValObj != null ){
1345                                                   String thisVal = thisValObj.toString();
1346                                                   if( thisVal.equals(filtPropVal) ){
1347                                                           return true;
1348                                                   }
1349                                           }
1350                                   }
1351                           }
1352                   }
1353                         
1354                   // Didn't find a match at the this level, so check the sets below it meet the criteria
1355                   if( resSet.getSubResultSet() != null ){
1356                           ListIterator<ResultSet> listItr = resSet.getSubResultSet().listIterator();
1357                           while( listItr.hasNext() ){
1358                                   if( filterMetByThisSet(listItr.next(), filtNodeType, filtPropName, filtPropVal) ){
1359                                           return true;
1360                                   }
1361                           }
1362                   }
1363                   
1364                   return false;
1365                   
1366           }// end of filterMetByThisSet()
1367           
1368
1369         
1370         /**
1371          * Collapse for do not output.
1372          *
1373          * @param resSetVal the res set val
1374          * @return the array list
1375          * @throws AAIException the AAI exception
1376          */
1377         public List<ResultSet> collapseForDoNotOutput(ResultSet resSetVal )
1378                 throws AAIException {
1379                 
1380                 // Given a ResultSet -- if it is tagged to NOT be output, then replace it with 
1381                 // it's sub-ResultSets if it has any. 
1382                 List<ResultSet> colResultSet = new ArrayList<>();
1383                 
1384                 if( resSetVal.getDoNotOutputFlag().equals("true") ){
1385                         // This ResultSet isn't to be displayed, so replace it with it's sub-ResultSets
1386                         List<ResultSet> subResList = (ArrayList<ResultSet>) resSetVal.getSubResultSet();
1387                         for( int k = 0; k < subResList.size(); k++ ){
1388                                 List<ResultSet> newSubResList =  collapseForDoNotOutput(subResList.get(k));
1389                                 colResultSet.addAll(newSubResList);
1390                         }
1391                 }
1392                 else {
1393                         // This set will be displayed
1394                         colResultSet.add(resSetVal);
1395                 }
1396                 
1397                 // For each result set now at this level, call this same routine to collapse their sub-resultSets
1398                 for( int i = 0; i < colResultSet.size(); i++ ){
1399                         List<ResultSet> newSubSet = new ArrayList<>();
1400                         List<ResultSet> subResList = (ArrayList<ResultSet>) colResultSet.get(i).getSubResultSet();
1401                         for( int n = 0; n < subResList.size(); n++ ){
1402                                 List<ResultSet> newSubResList =  collapseForDoNotOutput(subResList.get(n));
1403                                 newSubSet.addAll(newSubResList);
1404                         }
1405                         // Replace the old subResultSet with the collapsed set
1406                         colResultSet.get(i).setSubResultSet(newSubSet);
1407                 }
1408                 
1409                 return colResultSet;
1410                 
1411         }// End collapseForDoNotOutput()
1412         
1413         
1414     
1415         /**
1416          * Collect instance data.
1417          *
1418          * @param transId the trans id
1419          * @param fromAppId the from app id
1420          * @param thisLevelElemVtx the element vtx at this level 
1421          * @param thisVertsTrail the this verts trail
1422          * @param elementLocationTrail -- trail of nodeTypes that got us here (this element vertex) from the top
1423          * @param validNextStepMap the valid next step map -- hash of valid next steps (node types) for this model
1424          * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point
1425          * @param levelCounter the level counter
1426          * @param delKeyHash -- hashMap of which spots on our topology should be deleted during a modelDelete
1427          * @param namedQueryElementHash - hashMap which maps each spot in our widget topology to the NamedQueryElemment that it maps to
1428          * @param apiVer the api ver
1429          * @return resultSet
1430          * @throws AAIException the AAI exception
1431          */
1432         public ResultSet collectInstanceData(String transId, String fromAppId,
1433                                          Vertex thisLevelElemVtx,
1434                                          String thisVertsTrail,
1435                                          Multimap<String,String> validNextStepMap,
1436                                          List<String> vidsTraversed,
1437                                          int levelCounter,
1438                                          Map<String,String> delKeyHash,  // only applies when collecting data using the default model for delete
1439                                          Map<String,String> namedQueryElementHash,  // only applies to named-query data collecting
1440                                          String apiVer
1441                   )   throws AAIException {
1442                 
1443           levelCounter++;
1444           
1445           String thisElemVid = thisLevelElemVtx.id().toString();
1446            
1447           if( levelCounter > MAX_LEVELS ) {
1448                   throw new AAIException("AAI_6125", "collectInstanceData() has looped across more levels than allowed: " + MAX_LEVELS + ". ");
1449           }
1450           
1451           ResultSet rs = new ResultSet();
1452           if( namedQueryElementHash.containsKey(thisVertsTrail) ){
1453                   // We're collecting data for a named-query, so need to see if we need to do anything special
1454                   String nqElUuid = namedQueryElementHash.get(thisVertsTrail);
1455                   Vertex nqElementVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query-element",
1456                                         "named-query-element-uuid", nqElUuid);
1457                  
1458                   String tmpDoNotShow = nqElementVtx.<String>property("do-not-output").orElse(null);
1459                   if( tmpDoNotShow != null && tmpDoNotShow.equals("true") ){
1460                           rs.setDoNotOutputFlag("true");
1461                   }
1462                                   
1463                   if( namedQueryConstraintSaysStop(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer) ){
1464                           // There was a property constraint which says they do not want to collect this vertex or whatever 
1465                           // might be below it.  Just return the empty rs here.
1466                           return rs;
1467                   }
1468                   
1469                   String propLimDesc = nqElementVtx.<String>property("property-limit-desc").orElse(null);
1470                   if( (propLimDesc != null) && !propLimDesc.equals("") ){
1471                           if (propLimDesc.equalsIgnoreCase("show-all")) {
1472                                   rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_ALL);
1473                           } else if (propLimDesc.equalsIgnoreCase("show-none")) {
1474                                   rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_NONE);
1475                           }else if (propLimDesc.equalsIgnoreCase("name-and-keys-only")) {
1476                                   rs.setPropertyLimitDesc(PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY);
1477                           }
1478                   }
1479
1480                   // Look to see if we need to use an Override of the normal properties
1481                   Map<String,Object> tmpPropertyOverRideHash = getNamedQueryPropOverRide(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer);
1482                   //System.out.println(" DEBUG --- USING this propertyOverride data set on ResSet [" + tmpPropertyOverRideHash.toString() + "]");
1483                   rs.setPropertyOverRideHash(tmpPropertyOverRideHash);
1484                   
1485                   // See if we need to look up any "unconnected" data that needs to be associated with this result set
1486                   Map<String,Object> tmpExtraPropHash = getNamedQueryExtraDataLookup(transId, fromAppId, nqElementVtx, thisLevelElemVtx, apiVer);
1487                   //System.out.println(" DEBUG --- ADDING this EXTRA Lookup data to the ResSet [" + tmpExtraPropHash.toString() + "]");
1488                   rs.setExtraPropertyHash(tmpExtraPropHash);
1489           }
1490           
1491           rs.setVert(thisLevelElemVtx);
1492           rs.setLocationInModelSubGraph(thisVertsTrail);
1493           if( delKeyHash.containsKey(thisVertsTrail) && delKeyHash.get(thisVertsTrail).equals("T") ){
1494                   rs.setNewDataDelFlag("T");
1495           }
1496           else {
1497                   rs.setNewDataDelFlag("F");
1498           }
1499                 
1500           // Use Gremlin-pipeline to just look for edges that go to a valid "next-steps"
1501           Collection <String> validNextStepColl = validNextStepMap.get(thisVertsTrail);
1502           
1503           // Because of how we process linkage-points, we may have duplicate node-types in our next-stepMap (for one step)
1504           // So, to keep from looking (and bringing back) the same data twice, we need to make sure our next-steps are unique
1505           Set<String> validNextStepHashSet = new HashSet<>();
1506           Iterator <String> ntcItr = validNextStepColl.iterator();
1507           while( ntcItr.hasNext() ){
1508                   String targetStepStr = ntcItr.next();
1509                   validNextStepHashSet.add(targetStepStr);
1510           }
1511           
1512           List<String> tmpVidsTraversedList = new ArrayList<>();
1513           tmpVidsTraversedList.addAll(vidsTraversed);
1514           tmpVidsTraversedList.add(thisElemVid);
1515           
1516           Iterator <String> ntItr = validNextStepHashSet.iterator();
1517           while( ntItr.hasNext() ){
1518                   String targetStep = ntItr.next();
1519                   // NOTE: NextSteps can either be just a nodeType, or can be a nodeType plus
1520                   //     model-invariant-id-local and model-version-id-local (the two persona properties) 
1521                   //     if those need to be checked also.
1522                   //     When the persona stuff is part of the step, it is a comma separated string.
1523                   //     Ie.  "nodeType,model-inv-id-local,model-version-id-local" (the two "persona" props)
1524                   //     
1525                   String targetNodeType = "";
1526                   String pmid = "";
1527                   String pmv = "";
1528                   Boolean stepIsJustNT = true;
1529                   if( targetStep.contains(",") ){
1530                           stepIsJustNT = false;
1531                           String[] pieces = targetStep.split(",");
1532                           if( pieces.length != 3 ){
1533                                                 throw new AAIException("AAI_6128", "Unexpected format for nextStep in model processing = ["
1534                                                           + targetStep + "]. ");
1535                           }
1536                           else {
1537                                   targetNodeType = pieces[0];
1538                                   pmid = pieces[1];
1539                                   pmv = pieces[2];
1540                           }
1541                   }
1542                   else {
1543                           // It's just the nodeType with no other info
1544                           targetNodeType = targetStep;
1545                   }
1546                   
1547                   GraphTraversal<Vertex, Vertex> modPipe = null;
1548                   if( stepIsJustNT ){
1549                           modPipe = this.engine.asAdmin().getReadOnlyTraversalSource().V(thisLevelElemVtx).both().has(AAIProperties.NODE_TYPE, targetNodeType);
1550                   }
1551                   else {
1552                           modPipe = this.engine.asAdmin().getReadOnlyTraversalSource().V(thisLevelElemVtx).both().has(AAIProperties.NODE_TYPE, targetNodeType).has(addDBAliasedSuffix("model-invariant-id"),pmid).has(addDBAliasedSuffix("model-version-id"),pmv);
1553                   }
1554                   
1555                   if( modPipe == null || !modPipe.hasNext() ){
1556                          //System.out.println("DEBUG - didn't find any [" + targetStep + "] connected to this guy (which is ok)");
1557                   }
1558                   else {
1559                           while( modPipe.hasNext() ){
1560                                   Vertex tmpVert = (Vertex) modPipe.next();
1561                                   String tmpVid = tmpVert.id().toString();
1562                                   String tmpTrail = thisVertsTrail + "|" + targetStep;
1563                                   if( !vidsTraversed.contains(tmpVid) ){
1564                                           // This is one we would like to use - so we'll include the result set we get for it 
1565                                           ResultSet tmpResSet  = collectInstanceData( transId, fromAppId,
1566                                                                 tmpVert, tmpTrail, 
1567                                                                 validNextStepMap, tmpVidsTraversedList, 
1568                                                                 levelCounter, delKeyHash, namedQueryElementHash, apiVer );
1569                                           
1570                                           rs.getSubResultSet().add(tmpResSet);
1571                                   }
1572                           }
1573                   }
1574           }
1575         
1576           return rs;
1577           
1578         } // End of collectInstanceData()
1579
1580
1581         /**
1582          * Gen topo map 4 model.
1583          *
1584          * @param transId the trans id
1585          * @param fromAppId the from app id
1586          * @param modelVerVertex the model-ver vertex
1587          * @param modelVerId the model-version-id
1588          * @param loader the db maps
1589          * @return MultiMap of valid next steps for each potential model-element
1590          * @throws AAIException the AAI exception
1591          */
1592         public Multimap<String, String> genTopoMap4ModelVer(String transId, String fromAppId,
1593                                                         Vertex modelVerVertex, String modelVerId)
1594                                   throws AAIException {
1595           
1596                 if( modelVerVertex == null ){
1597                         throw new AAIException("AAI_6114", "null modelVerVertex passed to genTopoMap4ModelVer()");
1598                 }
1599                 
1600                 Multimap<String, String> initialEmptyMap = ArrayListMultimap.create();
1601                 List<String> vidsTraversed = new ArrayList<>();
1602                 String modelType = getModelTypeFromModelVer( modelVerVertex, "" );
1603                 if( modelType.equals("widget") ){
1604                         // A widget model by itself does not have a topoplogy.  That is - it has no "model-elements" which
1605                         // define how it is connected to other things.   All it has is a name which ties it to 
1606                         // an aai-node-type
1607                         Iterator<Vertex> vertI= this.traverseIncidentEdges(EdgeType.TREE, modelVerVertex, "model-element");
1608                         if( vertI != null && vertI.hasNext() ){
1609                                 throw new AAIException("AAI_6132", "Bad Model Definition: Widget Model has a startsWith edge to a model-element.  "
1610                                                 + " model-version-id = " + modelVerId);
1611                         }
1612                         else {
1613                                 return initialEmptyMap;
1614                         }
1615                 }
1616                 
1617                 String firstModelVerId = modelVerVertex.<String>property("model-version-id").orElse(null);
1618                 String firstModelVersion = modelVerVertex.<String>property("model-version").orElse(null);
1619                 if( firstModelVerId == null || firstModelVerId.equals("") || firstModelVersion == null || firstModelVersion.equals("") ){
1620                         throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-version-id or model-version.  model-version-id = "
1621                                   + modelVerId);
1622                 }
1623                 
1624                 Vertex firstElementVertex = getTopElementForSvcOrResModelVer( modelVerVertex, "" );
1625                 Vertex firstEleModVerVtx = getModelVerThatElementRepresents( firstElementVertex, "" );
1626                 String firstElemModelType = getModelTypeFromModelVer( firstEleModVerVtx, "" );    
1627                 if( ! firstElemModelType.equals("widget") ){
1628                         throw new AAIException("AAI_6132", "Bad Model Definition: First element must correspond to a widget type model.  Model UUID = "
1629                                   + modelVerId);
1630                 }
1631                 
1632                 Vertex firstModVtx = getModelGivenModelVer( modelVerVertex, "" );
1633             String firstModelInvId = firstModVtx.<String>property("model-invariant-id").orElse(null);
1634             if( firstModelInvId == null || firstModelInvId.equals("") ){
1635                         throw new AAIException("AAI_6132", "Bad Model Definition: Could not find model.model-invariant-id given model-ver.model-version-id = "
1636                                   + modelVerId);
1637                 }
1638             
1639                  Multimap<String, String> collectedMap = collectTopology4ModelVer( transId, fromAppId,
1640                                 firstElementVertex, "", initialEmptyMap, vidsTraversed, 0, null, firstModelInvId, firstModelVersion );
1641                  
1642                  return collectedMap;
1643           
1644         } // End of genTopoMap4ModelVer()
1645
1646
1647         public List<String> makeSureItsAnArrayList( String listStringVal ){
1648                 // We're sometimes getting a String back on db properties that should be ArrayList<String>
1649                 // Seems to be how they're defined in OXM - whether they use a "xml-wrapper" or not
1650                 // Need to translate them into ArrayLists sometimes...
1651                 
1652                 List<String> retArrList = new ArrayList<String>();
1653                 String listString = listStringVal;
1654                 listString = listString.replace(" ",  "");
1655                 listString = listString.replace("\"",  "");
1656                 listString = listString.replace("[",  "");
1657                 listString = listString.replace("]",  "");
1658                 String [] pieces = listString.split(",");
1659                 if( pieces != null && pieces.length > 0 ){
1660                         for( int i = 0; i < pieces.length; i++ ){
1661                                 retArrList.add(pieces[i]);
1662                         }
1663                 }
1664                         
1665                 return retArrList;
1666         }
1667
1668
1669         /**
1670          * Gets the mod constraint hash.
1671          *
1672          * @param modelElementVtx the model element vtx
1673          * @param currentHash -- the current ModelConstraint's that this routine will add to if it finds any.
1674          * @return HashMap of model-constraints that will be looked at for this model-element and what's "below" it.
1675          * @throws AAIException the AAI exception
1676          */
1677         public Map<String, Vertex> getModConstraintHash(Vertex modelElementVtx, Map<String, Vertex> currentHash )
1678                                   throws AAIException {
1679         
1680                 // For a given model-element vertex, look to see if there are any "model-constraint" elements that is has 
1681                 //   an OUT "uses" edge to.   If it does, then get any "constrained-element-set" nodes that are pointed to
1682                 //   by the "model-constraint".   That will be the replacement "constrained-element-set".  The UUID of the
1683                 //   "constrained-element-set" that it is supposed to replace is found in the property:
1684                 //   model-constraint.constrained-element-set-uuid-to-replace   
1685                 //
1686                 //   For now, that is the only type of model-constraint allowed, so that is all we will look for.  
1687                 //   Pass back any of these "constrained-element-set" nodes along with any that were passed in by 
1688                 //   the "currentHash" parameter.
1689                                 
1690                 if( modelElementVtx == null ){
1691                         String msg = " null modelElementVtx passed to getModConstraintHash() ";
1692                         throw new AAIException("AAI_6114", msg);
1693                 }
1694           
1695                 String modelType = modelElementVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1696                 if( modelType == null || (!modelType.equals("model-element")) ){
1697                         String msg = " getModConstraintHash() called with wrong type model: [" + modelType + "]. ";
1698                         throw new AAIException("AAI_6114", msg);
1699                 }
1700           
1701                 Map<String, Vertex> thisHash = new HashMap<>();
1702                 if( currentHash != null ){
1703                         thisHash.putAll(currentHash);
1704                 }
1705          
1706                 int count = 0;
1707                 List<Vertex> modelConstraintArray = new ArrayList<>();
1708                 Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, modelElementVtx, "model-constraint");
1709                 while( vertI != null && vertI.hasNext() ){
1710                         Vertex tmpVert = vertI.next();
1711                         String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1712                         if( (connectToType != null) && connectToType.equals("model-constraint") ){
1713                                 // We need to find the constrained element set pointed to by this and add it to the Hash to return
1714                                 modelConstraintArray.add(tmpVert);
1715                                 count++;
1716                         }
1717                 }
1718           
1719                 if( count > 0 ) {
1720                         for( int i = 0; i < count; i++ ){
1721                                 Vertex vtxOfModelConstraint = modelConstraintArray.get(i);
1722                                 String uuidOfTheOneToBeReplaced = vtxOfModelConstraint.<String>property("constrained-element-set-uuid-2-replace").orElse(null);
1723                                 // We have the UUID of the constrained-element-set that will be superseded, now find the
1724                                 // constrained-element-set to use in its place
1725                                 Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.TREE, vtxOfModelConstraint, "constrained-element-set");
1726                                 while( mvertI != null && mvertI.hasNext() ){
1727                                         // There better only be one...  
1728                                         Vertex tmpVert = mvertI.next();
1729                                         String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1730                                         if( (connectToType != null) && connectToType.equals("constrained-element-set") ){
1731                                                 // This is the "constrained-element-set" that we want to use as the Replacement
1732                                                 thisHash.put(uuidOfTheOneToBeReplaced, tmpVert );
1733                                         }
1734                                 }
1735                         }
1736                         return thisHash;
1737                 }
1738                 else {
1739                         // Didn't find anything to add, so just return what they passed in.
1740                         return currentHash;
1741                 }
1742         
1743         } // End of getModConstraintHash()
1744  
1745         
1746         /**
1747          * Gets the top element vertex for service or resource model.
1748          *
1749          * @param modelVerVtx the model-ver vertex
1750          * @return first element pointed to by this model-ver
1751          * @throws AAIException the AAI exception
1752          */
1753         public Vertex getTopElementForSvcOrResModelVer(Vertex modelVerVtx, String trail )
1754                                   throws AAIException {
1755         
1756                   // For a "resource" or "service" type model, return the "top" element in that model
1757                   if( modelVerVtx == null ){
1758                           String msg = " null modelVertex passed to getTopoElementForSvcOrResModelVer() at [" + trail + "]. ";
1759                           throw new AAIException("AAI_6114", msg);
1760                   }
1761                  
1762                   String modelVerId = modelVerVtx.<String>property("model-version-id").orElse(null);
1763                   if( modelVerId == null ){
1764                           String nt = modelVerVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
1765                           if( nt != null && !nt.equals("model-ver") ){
1766                                   String msg = "Illegal model defined: model element pointing to nodeType: [" 
1767                                                   + nt + "], should be pointing to: [model-ver] at [" + trail + "]. ";
1768                                   throw new AAIException("AAI_6132", msg);
1769                           }
1770                   }
1771                   
1772                   Vertex firstElementVertex = null;
1773         
1774                   Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, modelVerVtx, "model-element");
1775                   int elCount = 0;
1776                   while( vertI != null && vertI.hasNext() ){
1777                           elCount++;
1778                           firstElementVertex = vertI.next();
1779                   }
1780                         
1781                   if( elCount > 1 ){
1782                           String msg = "Illegal model defined: More than one first element defined for model-ver-id = " + 
1783                                           modelVerId + " at [" + trail + "]. ";
1784                           throw new AAIException("AAI_6132", msg);
1785                   }
1786                           
1787                   if( firstElementVertex == null ){
1788                           String msg = "Could not find first model element for model-ver-id = " 
1789                                           + modelVerId + " at [" + trail + "]. ";
1790                           throw new AAIException("AAI_6132", msg);
1791                   }
1792                   
1793                   return firstElementVertex;
1794           
1795         } // End of getTopElementForSvcOrResModelVer()
1796
1797         
1798                  
1799         /**
1800          * Gets the named query prop over ride.
1801          *
1802          * @param transId the trans id
1803          * @param fromAppId the from app id
1804          * @param namedQueryElementVertex the named query element vertex
1805          * @param instanceVertex the instance vertex
1806          * @param apiVer the api ver
1807          * @return HashMap of alternate properties to return for this element
1808          * @throws AAIException the AAI exception
1809          */
1810         public Map<String,Object> getNamedQueryPropOverRide(String transId, String fromAppId,
1811                                                         Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer )
1812                                   throws AAIException {
1813                 
1814                 // If this model-element says that they want an alternative set of properties returned, then pull that
1815                 // data out of the instance vertex.
1816                 
1817                 Map<String,Object> altPropHash = new HashMap<>();
1818                         
1819                 if( namedQueryElementVertex == null ){
1820                         String msg = " null namedQueryElementVertex passed to getNamedQueryPropOverRide() ";
1821                         throw new AAIException("AAI_6114", msg);
1822                 }
1823                 
1824                 List<String> propCollectList = new ArrayList<>();
1825                 Iterator <VertexProperty<Object>> vpI = namedQueryElementVertex.properties("property-collect-list");
1826                 while( vpI.hasNext() ){
1827                         propCollectList.add((String)vpI.next().value());
1828                 }
1829                 
1830                 for( int i = 0; i < propCollectList.size(); i++ ){
1831                         String thisPropName = propCollectList.get(i);
1832                         Object instanceVal = instanceVertex.<Object>property(thisPropName).orElse(null);
1833                         altPropHash.put(thisPropName, instanceVal);
1834                 }
1835                         
1836                 return altPropHash;
1837           
1838         } // End of getNamedQueryPropOverRide()
1839
1840         
1841         /**
1842          * Named query constraint says stop.
1843          *
1844          * @param transId the trans id
1845          * @param fromAppId the from app id
1846          * @param namedQueryElementVertex the named query element vertex
1847          * @param instanceVertex the instance vertex
1848          * @param apiVer the api ver
1849          * @return true - if a constraint was defined that has not been met by the passed instanceVertex
1850          * @throws AAIException the AAI exception
1851          */
1852         public Boolean namedQueryConstraintSaysStop(String transId, String fromAppId,
1853                                                 Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer )
1854                                   throws AAIException {
1855                 
1856                 // For each (if any) property-constraint defined for this named-query-element, we will evaluate if
1857                 // the constraint is met or not-met.  if there are constraints and any are not-met, then
1858                 // we return "true".
1859                 
1860                 if( namedQueryElementVertex == null ){
1861                         String msg = " null namedQueryElementVertex passed to namedQueryConstraintSaysStop() ";
1862                         throw new AAIException("AAI_6114", msg);
1863                 }
1864                 if( instanceVertex == null ){
1865                         String msg = " null instanceVertex passed to namedQueryConstraintSaysStop() ";
1866                         throw new AAIException("AAI_6114", msg);
1867                 }
1868                                 
1869                 Iterator<Vertex> constrPipe = this.traverseIncidentEdges(EdgeType.TREE, namedQueryElementVertex, "property-constraint");
1870                 if( constrPipe == null || !constrPipe.hasNext() ){
1871                         // There's no "property-constraint" defined for this named-query-element.   No problem.
1872                         return false;
1873                 }
1874                 
1875                 while( constrPipe.hasNext() ){
1876                         Vertex constrVtx = (Vertex) constrPipe.next();
1877                         // We found a property constraint that we will need to check
1878                         String conType = constrVtx.<String>property("constraint-type").orElse(null);
1879                         if( (conType == null) || conType.equals("")){
1880                                 String msg = " Bad property-constraint (constraint-type) found in Named Query definition. ";
1881                                 throw new AAIException("AAI_6133", msg);
1882                         }
1883                         String propName = constrVtx.<String>property("property-name").orElse(null);
1884                         if( (propName == null) || propName.equals("")){
1885                                 String msg = " Bad property-constraint (property-name) found in Named Query definition. ";
1886                                 throw new AAIException("AAI_6133", msg);
1887                         }
1888                         String propVal = constrVtx.<String>property("property-value").orElse(null);
1889                         if( (propVal == null) || propVal.equals("")){
1890                                 String msg = " Bad property-constraint (propVal) found in Named Query definition. ";
1891                                 throw new AAIException("AAI_6133", msg);
1892                         }
1893                         
1894                         // See if that constraint is met or not
1895                         String val = instanceVertex.<String>property(propName).orElse(null);
1896                         if( val == null ){
1897                                 val = "";
1898                         }
1899                         
1900                         if( conType.equals("EQUALS") ){
1901                                 if( !val.equals(propVal) ){
1902                                         // This constraint was not met
1903                                         return true;
1904                                 }
1905                         }
1906                         else if( conType.equals("NOT-EQUALS") ){
1907                                 if( val.equals(propVal) ){
1908                                         // This constraint was not met
1909                                         return true;
1910                                 }
1911                         }
1912                         else {
1913                                 String msg = " Bad property-constraint (constraint-type) found in Named Query definition. ";
1914                                 throw new AAIException("AAI_6133", msg);
1915                         }
1916                 }
1917                  
1918                 return false;
1919           
1920         } // End of namedQueryConstraintSaysStop()
1921
1922         
1923         /**
1924          * Gets the named query extra data lookup.
1925          *
1926          * @param transId the trans id
1927          * @param fromAppId the from app id
1928          * @param namedQueryElementVertex the named query element vertex
1929          * @param instanceVertex the instance vertex
1930          * @param apiVer the api ver
1931          * @return HashMap of alternate properties to return for this element
1932          * @throws AAIException the AAI exception
1933          */
1934         public Map<String,Object> getNamedQueryExtraDataLookup(String transId, String fromAppId,
1935                                                            Vertex namedQueryElementVertex, Vertex instanceVertex, String apiVer )
1936                                   throws AAIException {
1937                 
1938                 // For each (if any) related-lookup defined for this named-query-element, we will go and
1939                 // and try to find it.  All the related-lookup data will get put in a hash and returned.
1940                 
1941                 if( namedQueryElementVertex == null ){
1942                         String msg = " null namedQueryElementVertex passed to getNamedQueryExtraDataLookup() ";
1943                         throw new AAIException("AAI_6114", msg);
1944                 }
1945                 if( instanceVertex == null ){
1946                         String msg = " null instanceVertex passed to getNamedQueryExtraDataLookup() ";
1947                         throw new AAIException("AAI_6114", msg);
1948                 }
1949                 
1950                 Map<String,Object> retHash = new HashMap<>();
1951                 
1952                 Iterator<Vertex> lookPipe = this.traverseIncidentEdges(EdgeType.TREE, namedQueryElementVertex, "related-lookup");
1953                 if( lookPipe == null || !lookPipe.hasNext() ){
1954                         // There's no "related-lookup" defined for this named-query-element.   No problem.
1955                         return retHash;
1956                 }
1957                 
1958                 while( lookPipe.hasNext() ){
1959                         Vertex relLookupVtx = (Vertex) lookPipe.next();
1960                         // We found a related-lookup record to try and use
1961                         String srcProp = relLookupVtx.<String>property("source-node-property").orElse(null);
1962                         String srcNodeType = relLookupVtx.<String>property("source-node-type").orElse(null);
1963                         srcProp = getPropNameWithAliasIfNeeded(srcNodeType, srcProp);
1964                         
1965                         if( (srcProp == null) || srcProp.equals("")){
1966                                 String msg = " Bad related-lookup (source-node-property) found in Named Query definition. ";
1967                                 throw new AAIException("AAI_6133", msg);
1968                         }
1969                         String targetNodeType = relLookupVtx.<String>property("target-node-type").orElse(null);
1970                         if( (targetNodeType == null) || targetNodeType.equals("")){
1971                                 String msg = " Bad related-lookup (targetNodeType) found in Named Query definition. ";
1972                                 throw new AAIException("AAI_6133", msg);
1973                         }
1974                         String targetProp = relLookupVtx.<String>property("target-node-property").orElse(null);
1975                         targetProp = getPropNameWithAliasIfNeeded(targetNodeType, targetProp);
1976                                         
1977                         if( (targetProp == null) || targetProp.equals("")){
1978                                 String msg = " Bad related-lookup (target-node-property) found in Named Query definition. ";
1979                                 throw new AAIException("AAI_6133", msg);
1980                         }
1981                         
1982                         List<String> propCollectList = new ArrayList<>();
1983                         Iterator <VertexProperty<Object>> vpI = relLookupVtx.properties("property-collect-list");
1984                         while( vpI.hasNext() ){
1985                                 propCollectList.add((String)vpI.next().value());
1986                         }
1987
1988                         // Use the value from the source to see if we can find ONE target record using the 
1989                         //     value from the source
1990                         String valFromInstance = instanceVertex.<String>property(srcProp).orElse(null);
1991                         if( valFromInstance == null ){
1992                                 // if there is no key to use to go look up something, we should end it here and just
1993                                 // note what happened  - no need to try to look something up by an empty key 
1994                                 LOGGER.debug("WARNING - the instance data node of type [" + srcNodeType 
1995                                                 + "] did not have a value for property [" + srcProp 
1996                                                 + "], so related-lookup is being abandoned.");
1997                                 return retHash;
1998                         }
1999                         
2000                         Map<String,Object> propHash = new HashMap<String,Object>();
2001                         propHash.put(targetProp, valFromInstance);
2002                         
2003                         Optional<Vertex> result = dbMethHelper.locateUniqueVertex(targetNodeType, propHash);
2004                         if (!result.isPresent()) {
2005                                 // If it can't find the lookup node, don't fail, just log that it couldn't be found ---
2006                                 LOGGER.debug("WARNING - Could not find lookup node that corresponds to nodeType [" 
2007                                                 + targetNodeType + "] propertyName = [" + srcProp 
2008                                                 + "], propVal = [" + valFromInstance
2009                                                 + "] so related-lookup is being abandoned.");
2010                                 return retHash;
2011                         }
2012                         else {
2013                         Vertex tmpVtx = result.get();
2014                         // Pick up the properties from the target vertex that they wanted us to get
2015                         for( int j = 0; j < propCollectList.size(); j++ ){
2016                                 String tmpPropName = propCollectList.get(j);
2017                                         tmpPropName = getPropNameWithAliasIfNeeded(targetNodeType, tmpPropName);
2018                                 Object valObj = tmpVtx.<Object>property(tmpPropName).orElse(null);
2019                                 String lookupKey = targetNodeType + "." + tmpPropName;
2020                                 retHash.put(lookupKey, valObj);
2021                                 
2022                         }
2023                 }
2024                 }
2025                  
2026                 return retHash;
2027           
2028         } // End of getNamedQueryExtraDataLookup()
2029
2030         /**
2031          * Collect NQ element hash.
2032          *
2033          * @param transId the trans id
2034          * @param fromAppId the from app id
2035          * @param thisLevelElemVtx the element verrtx for this level 
2036          * @param incomingTrail the incoming trail -- trail of nodeTypes that got us here (this nq-element vertex) from the top
2037          * @param currentHash the current hash
2038      * @param Map that got us to this point (that we will use as the base of the map we will return)
2039          * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point
2040          * @param levelCounter the level counter
2041          * @return HashMap of all widget-points on a namedQuery topology with the value being the "named-query-element-uuid" for that spot.
2042          * @throws AAIException the AAI exception
2043          */
2044         public Map<String, String> collectNQElementHash(String transId, String fromAppId,
2045                                                     Vertex thisLevelElemVtx, String incomingTrail,
2046                                                     Map<String,String> currentHash, ArrayList <String> vidsTraversed,
2047                                                     int levelCounter )   throws AAIException {
2048         
2049           levelCounter++;
2050
2051           Map<String, String> thisHash = new HashMap<>();
2052           thisHash.putAll(currentHash);
2053          
2054           if( levelCounter > MAX_LEVELS ) {
2055                   throw new AAIException("AAI_6125", "collectNQElementHash() has looped across more levels than allowed: " + MAX_LEVELS + ". ");
2056           }
2057           String thisGuysTrail = "";
2058           String thisElemVid = thisLevelElemVtx.id().toString();
2059          
2060           // Find out what widget (and thereby what aai-node-type) this element represents.
2061           String thisElementNodeType = getNqElementWidgetType( transId, fromAppId,  thisLevelElemVtx, incomingTrail );
2062           
2063           if( incomingTrail == null || incomingTrail.equals("") ){
2064                   // This is the first one
2065                   thisGuysTrail = thisElementNodeType;
2066           }
2067           else {
2068                   thisGuysTrail = incomingTrail + "|" + thisElementNodeType;
2069           }
2070           vidsTraversed.add(thisElemVid);
2071           
2072           String nqElementUuid = thisLevelElemVtx.<String>property("named-query-element-uuid").orElse(null);
2073           if( nqElementUuid == null || nqElementUuid.equals("") ){
2074                   String msg = " named-query element UUID not found at trail = [" + incomingTrail + "].";
2075                   throw new AAIException("AAI_6133", msg);
2076           }
2077           thisHash.put(thisGuysTrail, nqElementUuid ); 
2078           
2079           //  Now go "down" and look at the sub-elements pointed to so we can get their data.
2080           Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, thisLevelElemVtx, "named-query-element");
2081           while( vertI != null && vertI.hasNext() ){
2082                   Vertex tmpVert = vertI.next();
2083                   String vid = tmpVert.id().toString();
2084                   Map<String,Object> elementHash = new HashMap<String, Object>();
2085                   
2086                   String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2087                   if( connectToType != null && connectToType.equals("named-query-element") ){
2088                           // This is what we would expect
2089                           elementHash.put(vid, tmpVert);
2090                   }
2091                   else {
2092                           String msg = " named query element has [connectedTo] edge to improper nodeType= [" 
2093                                           + connectToType + "] trail = [" + incomingTrail + "].";
2094                           throw new AAIException("AAI_6133", msg);
2095                   }
2096                   for( Map.Entry<String, Object> entry : elementHash.entrySet() ){
2097                           Vertex elVert = (Vertex)(entry.getValue());
2098                           String tmpElVid = elVert.id().toString();
2099                           if( !vidsTraversed.contains(tmpElVid) ){
2100                                   // This is one we would like to use - so we'll recursively get it's result set to add to ours
2101                                   Map<String, String> tmpHash = collectNQElementHash( transId, fromAppId, 
2102                                                         elVert, thisGuysTrail, currentHash, vidsTraversed, levelCounter);
2103                                   thisHash.putAll(tmpHash);
2104                           }
2105                   }             
2106           }
2107           return thisHash;
2108           
2109         } // End of collectNQElementHash()
2110         
2111
2112         /**
2113          * Collect delete key hash.
2114          *
2115          * @param transId the trans id
2116          * @param fromAppId the from app id
2117          * @param thisLevelElemVtx the element vertex at this level
2118          * @param incomingTrail the incoming trail -- trail of nodeTypes that got us here (this vertex) from the top
2119          * @param currentHash the current hash
2120      * @param Map that got us to this point (that we will use as the base of the map we will return)
2121          * @param vidsTraversed the vids traversed ---- ArrayList of vertexId's that we traversed to get to this point
2122          * @param levelCounter the level counter
2123          * @param loader the db maps
2124          * @param modConstraintHash the mod constraint hash
2125          * @param overRideModelId the over ride model id
2126          * @param overRideModelVersionId the over ride model version id
2127          * @return HashMap of all widget-points on a model topology with the value being the "newDataDelFlag" for that spot.
2128          * @throws AAIException the AAI exception
2129          */
2130         public Map<String, String> collectDeleteKeyHash(String transId, String fromAppId,
2131                                                     Vertex thisLevelElemVtx, String incomingTrail,
2132                                                     Map<String,String> currentHash, ArrayList <String> vidsTraversed,
2133                                                     int levelCounter, Map<String, Vertex> modConstraintHash,
2134                                                     String overRideModelId, String overRideModelVersionId )
2135                                   throws AAIException {
2136         
2137           levelCounter++;
2138
2139           Map<String, String> thisHash = new HashMap<>();
2140           thisHash.putAll(currentHash);
2141          
2142           if( levelCounter > MAX_LEVELS ) {
2143                   throw new AAIException("AAI_6125", "collectDeleteKeyHash() has looped across more levels than allowed: " + MAX_LEVELS + ". ");
2144           }
2145           String thisGuysTrail = "";
2146           String thisElemVid = thisLevelElemVtx.id().toString();
2147           Map<String, Vertex> modConstraintHash2Use = null;
2148           
2149           // If this element represents a resource or service model, then we will replace this element with 
2150           //    the "top" element of that resource or service model.  That model-element already points to its
2151           //    topology, so it will graft in that model's topology.   
2152           // EXCEPT - if this element has "linkage-points" defined, then we need to do some extra
2153           //     processing for how we join to that model and will not try to go any "deeper".
2154           List<String> linkagePtList = new ArrayList<>();
2155           Iterator <VertexProperty<Object>> vpI = thisLevelElemVtx.properties("linkage-points");
2156
2157           // I am not sure why, but since "linkage-points" is an xml-element-wrapper in the OXM definition, 
2158           // we get back the whole array of Strings in one String - but still use the "vtx.properties()" to
2159           // get it - but only look at the first thing returned by the iterator.
2160           if( vpI.hasNext() ){
2161                   String tmpLinkageThing = (String)vpI.next().value();
2162                   linkagePtList = makeSureItsAnArrayList( tmpLinkageThing );
2163           }   
2164                   
2165           if( linkagePtList != null && !linkagePtList.isEmpty() ){
2166                   // Whatever this element is - we are connecting to it via a linkage-point
2167                   // We will figure out what to do and then return without going any deeper
2168                   String elemFlag = thisLevelElemVtx.<String>property("new-data-del-flag").orElse(null);
2169                   
2170                   Set<String> linkageConnectNodeTypes = getLinkageConnectNodeTypes( linkagePtList );
2171                   Iterator <?> linkNtIter = linkageConnectNodeTypes.iterator();
2172                   String incTrail = "";
2173                   if( incomingTrail != null &&  !incomingTrail.equals("") ){
2174                           incTrail = incomingTrail + "|";
2175                   }
2176                   
2177                   while( linkNtIter.hasNext() ){
2178                           // The 'trail' (or trails) for this element should just be the to the first-contact on the linkage point
2179                           String linkTrail = incTrail + linkNtIter.next();
2180                           Boolean alreadyTaggedFalse = false;
2181                           if( thisHash.containsKey(linkTrail) && thisHash.get(linkTrail).equals("F") ){
2182                                   // some other path with a matching trail has the deleteFlag set to "F", so we do not want
2183                                   // to override that since our model code only uses nodeTypes to know where it is - and we
2184                                   // would rather do less deleting than needed instead of too much deleting.
2185                                   alreadyTaggedFalse = true;
2186                           }
2187                           if( elemFlag != null && elemFlag.equals("T") && !alreadyTaggedFalse ){
2188                                   // This trail should be marked with an "T"
2189                                   thisHash.put(linkTrail, "T");
2190                           }
2191                           else {
2192                                   thisHash.put(linkTrail, "F");
2193                           }
2194                   }
2195                   return thisHash;
2196           }
2197   
2198           // ----------------------------------------------------------------------------
2199           // If we got to here, then this was not an element that used a linkage-point 
2200           // ----------------------------------------------------------------------------
2201           
2202           // Find out what widget-model (and thereby what aai-node-type) this element represents.
2203           // Even if this element is pointing to a service or resource model, it must have a
2204           // first element which is a single widget-type model.  
2205           String thisElementNodeType = getModElementWidgetType( thisLevelElemVtx, incomingTrail );
2206           String firstElementModelInfo = "";
2207
2208           vidsTraversed.add(thisElemVid);
2209           Vertex elementVtxForThisLevel = null;
2210           Vertex thisElementsModelVerVtx = getModelVerThatElementRepresents( thisLevelElemVtx, incomingTrail );
2211           Vertex thisElementsModelVtx = getModelGivenModelVer( thisElementsModelVerVtx, incomingTrail );
2212           String modType = getModelTypeFromModel( thisElementsModelVtx, incomingTrail );
2213           String subModelFirstModInvId = thisElementsModelVtx.<String>property("model-invariant-id").orElse(null);
2214           String subModelFirstVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null);
2215           if( modType.equals("widget") ){
2216                   if( overRideModelId != null && !overRideModelId.equals("") ){
2217                           // Note - this is just to catch the correct model for the TOP node in a model since
2218                           //    it will have an element which will always be a widget even though the model
2219                           //    could be a resource or service model.
2220                           firstElementModelInfo = "," + overRideModelId + "," + overRideModelVersionId;
2221                   }
2222           }     
2223           else if( nodeTypeSupportsPersona(thisElementNodeType) ){
2224                   firstElementModelInfo = "," + subModelFirstModInvId + "," + subModelFirstVerId;
2225           }
2226                           
2227           if( incomingTrail.equals("") ){
2228                   // This is the first one
2229                   thisGuysTrail = thisElementNodeType + firstElementModelInfo;
2230           }
2231           else {
2232                   thisGuysTrail = incomingTrail + "|" + thisElementNodeType  + firstElementModelInfo;
2233           }
2234           
2235           String tmpFlag = "F";
2236           Boolean stoppedByASvcOrResourceModelElement = false;
2237           if( modType.equals("widget") ){
2238                   elementVtxForThisLevel = thisLevelElemVtx;
2239                   // For the element-model for the widget at this level, record it's delete flag 
2240                   tmpFlag = elementVtxForThisLevel.<String>property("new-data-del-flag").orElse(null);
2241           }
2242           else {
2243                   // For an element that is referring to a resource or service model, we replace 
2244               // this element with the "top" element for that resource/service model so that the
2245                   // topology of that resource/service model will be included in this topology.
2246                   String modelVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null);
2247                   if( subModelFirstModInvId == null || subModelFirstModInvId.equals("") 
2248                                   || subModelFirstVerId == null || subModelFirstVerId.equals("") ){
2249                           throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-invariant-id or model-version-id.  Model-version-id = " +
2250                                   modelVerId + ", at [" + incomingTrail + "]");
2251                   }
2252                   
2253                   // BUT --  if the model-element HERE at the resource/service level does NOT have 
2254                   //    it's new-data-del-flag set to "T", then we do not need to go down into the 
2255                   //    sub-model looking for delete-able things.
2256
2257                   tmpFlag = thisLevelElemVtx.<String>property("new-data-del-flag").orElse(null);
2258                   elementVtxForThisLevel = getTopElementForSvcOrResModelVer(thisElementsModelVerVtx, thisGuysTrail);
2259                   if( tmpFlag != null && tmpFlag.equals("T") ){
2260                           modConstraintHash2Use = getModConstraintHash( thisLevelElemVtx, modConstraintHash );
2261                   }
2262                   else {
2263                           stoppedByASvcOrResourceModelElement = true;
2264                   }
2265                   // For the element-model for the widget at this level, record it's delete flag 
2266                   tmpFlag = elementVtxForThisLevel.<String>property("new-data-del-flag").orElse(null);
2267           }
2268           
2269           String flag2Use = "F";  // by default we'll use "F" for the delete flag
2270           if( ! stoppedByASvcOrResourceModelElement ){
2271                   // Since we haven't been stopped by a resource/service level "F", we can look at the lower level flag
2272                   if( thisHash.containsKey(thisGuysTrail) ){
2273                           // We've seen this spot in the topology before - do not override the delete flag if the older one is "F"
2274                           // We will only over-ride it if the old one was "T" and the new one is "F" (anything but "T")
2275                           String oldFlag = thisHash.get(thisGuysTrail);
2276                           if( oldFlag.equals("T") && (tmpFlag != null) && tmpFlag.equals("T") ){
2277                                   // The old flag was "T" and the new flag is also "T"
2278                                   flag2Use = "T";
2279                           }
2280                           else {
2281                                   // the old flag was not "F" - so don't override it
2282                                   flag2Use = "F";
2283                           }
2284                   }
2285                   else if( (tmpFlag != null) && tmpFlag.equals("T") ){
2286                           // We have not seen this one, so we can set it to "T" if that's what it is.
2287                           flag2Use = "T";
2288                   }
2289           }
2290           
2291           thisHash.put(thisGuysTrail, flag2Use); 
2292           if( ! stoppedByASvcOrResourceModelElement ){
2293                   // Since we haven't been stopped by a resource/service level "F", we will continue to
2294                   //     go "down" and look at the elements pointed to so we can get their data.
2295                   Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, elementVtxForThisLevel, "model-element", "constrained-element-set");
2296                    while( vertI != null && vertI.hasNext() ){
2297                           Vertex tmpVert = vertI.next();
2298                           String vid = tmpVert.id().toString();
2299                           Map<String,Object> elementHash = new HashMap<String, Object>();
2300                           
2301                           String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2302                           if( connectToType != null && connectToType.equals("model-element") ){
2303                                   // A nice, regular old model-element
2304                                   elementHash.put(vid, tmpVert);
2305                           }
2306                           else if( (connectToType != null) && connectToType.equals("constrained-element-set") ){
2307                                   // translate the constrained-element-set into a hash of model-element Vertex's
2308                                   String constrainedElementSetUuid = tmpVert.<String>property("constrained-element-set-uuid").orElse(null);
2309                                   if( (modConstraintHash2Use != null) && modConstraintHash2Use.containsKey(constrainedElementSetUuid) ){
2310                                           // This constrained-element-set is being superseded by a different one
2311                                           Vertex replacementConstraintVert = modConstraintHash.get(constrainedElementSetUuid);
2312                                           elementHash = getNextStepElementsFromSet( replacementConstraintVert );
2313                                           // Now that we've found and used the replacement constraint, we don't need to carry it along any farther
2314                                           modConstraintHash.remove(constrainedElementSetUuid);
2315                                   }
2316                                   else {
2317                                           elementHash = getNextStepElementsFromSet( tmpVert );    
2318                                   }
2319                           }
2320                           else {
2321                                   String msg = " model-element has [connectedTo] edge to improper nodeType= [" 
2322                                                   + connectToType + "] trail = [" + incomingTrail + "].";
2323                                   throw new AAIException("AAI_6132", msg);
2324                           }
2325                           
2326                           for( Map.Entry<String, Object> entry : elementHash.entrySet() ){
2327                                   Vertex elVert = (Vertex)(entry.getValue());
2328                                   String tmpElVid = elVert.id().toString();
2329                                   String tmpElNT = getModElementWidgetType( elVert, thisGuysTrail );
2330                                   check4EdgeRule(tmpElNT, thisElementNodeType);
2331                                   if( !vidsTraversed.contains(tmpElVid) ){
2332                                           // This is one we would like to use - so we'll recursively get it's result set to add to ours
2333                                           Map<String, String> tmpHash = collectDeleteKeyHash( transId, fromAppId, 
2334                                                                 elVert, thisGuysTrail, 
2335                                                                 currentHash, vidsTraversed, levelCounter, modConstraintHash2Use,
2336                                                                 "", "" );
2337                                           thisHash.putAll(tmpHash);
2338                                   }
2339                           }             
2340                   }
2341           }
2342           return thisHash;
2343           
2344         } // End of collectDeleteKeyHash()
2345         
2346         
2347         /**
2348          * Gets the linkage connect node types.
2349          *
2350          * @param linkagePtList the linkage pt list
2351          * @return the linkage connect node types
2352          * @throws AAIException the AAI exception
2353          */
2354         public Set<String> getLinkageConnectNodeTypes(List<String> linkagePtList )
2355                           throws AAIException {
2356                 // linkage points are a path from the top of a model to where we link in.  
2357                 // This method wants to just bring back a list of distinct last items.  
2358                 // Ie: for the input with these two:  "pserver|lag-link|l-interface" and "pserver|p-interface|l-interface"
2359                 //   it would just return a single item, "l-interface" since both linkage points end in that same node-type.
2360                 
2361                 Set<String> linkPtSet = new HashSet<>();
2362                 
2363                 if( linkagePtList == null ){
2364                         String detail = " Bad (null) linkagePtList passed to getLinkageConnectNodeTypes() ";
2365                         throw new AAIException("AAI_6125", detail);
2366                 }
2367                 
2368                 for( int i = 0; i < linkagePtList.size(); i++ ){
2369                         String [] trailSteps = linkagePtList.get(i).split("\\|");
2370                         if( trailSteps == null || trailSteps.length == 0 ){
2371                                 String detail = " Bad incomingTrail passed to getLinkageConnectNodeTypes(): [" + linkagePtList + "] ";
2372                                 throw new AAIException("AAI_6125", detail);
2373                         }
2374                         String lastStepNT = trailSteps[trailSteps.length - 1];
2375                         linkPtSet.add(lastStepNT);
2376                 }
2377                 
2378                 return linkPtSet;
2379         
2380         }// End getLinkageConnectNodeTypes()
2381         
2382         
2383         /**
2384          * Collect topology for model-ver.
2385          *
2386          * @param transId the trans id
2387          * @param fromAppId the from app id
2388          * @param modelElement vertex to collect for
2389          * @param incomingTrail the incoming trail -- trail of nodeTypes/personaInfo that got us here (this vertex) from the top
2390          * @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)
2391          * @param vidsTraversed the vids traversed -- ArrayList of vertexId's that we traversed to get to this point
2392          * @param levelCounter the level counter
2393          * @param loader the db maps
2394          * @param modConstraintHash the mod constraint hash
2395          * @param overRideModelInvId the override model-invariant-id
2396          * @param overRideModelVersionId the override model-version-id
2397          * @return Map of the topology
2398          * @throws AAIException the AAI exception
2399          */
2400         public Multimap<String, String> collectTopology4ModelVer(String transId, String fromAppId,
2401                                                              Vertex thisLevelElemVtx, String incomingTrail,
2402                                                              Multimap<String,String> currentMap, List<String> vidsTraversed,
2403                                                              int levelCounter, Map<String, Vertex> modConstraintHash,
2404                                                              String overRideModelInvId, String overRideModelVersionId )
2405                                   throws AAIException {
2406         
2407           levelCounter++;
2408
2409           Multimap<String, String> thisMap = ArrayListMultimap.create();
2410           thisMap.putAll(currentMap);
2411          
2412           if( levelCounter > MAX_LEVELS ) {
2413                   throw new AAIException("AAI_6125", "collectTopology4ModelVer() has looped across more levels than allowed: " + MAX_LEVELS + ". ");
2414           }
2415           String thisGuysTrail = "";
2416           String thisElemVid = thisLevelElemVtx.id().toString();
2417           Map<String, Vertex> modConstraintHash2Use = null;
2418  
2419           // If this element represents a resource or service model, then we will replace this element with 
2420           // the "top" element of that resource or service model.  That model-element already points to its
2421           // topology, so it will graft in that model's topology.   
2422           // EXCEPT - if this element defines "linkage-points" defined, then we need to do some extra
2423           //     processing for how we join to that model.
2424         
2425           // Find out what widget-model (and thereby what aai-node-type) this element represents.
2426           // Even if this element is pointing to a service or resource model, it must have a
2427           // first element which is a single widget-type model. 
2428           String firstElementModelInfo = "";
2429           String thisElementNodeType = getModElementWidgetType( thisLevelElemVtx, incomingTrail );
2430           if( nodeTypeSupportsPersona(thisElementNodeType) && overRideModelInvId != null && !overRideModelInvId.equals("") ){
2431                   firstElementModelInfo = "," + overRideModelInvId + "," + overRideModelVersionId;
2432           }
2433           
2434           Vertex elementVtxForThisLevel = null;
2435           Vertex thisElementsModelVerVtx = getModelVerThatElementRepresents( thisLevelElemVtx, incomingTrail );
2436           String subModelFirstModInvId = "";
2437           String subModelFirstModVerId = "";
2438           String modInfo4Trail = "";
2439           String modType = getModelTypeFromModelVer( thisElementsModelVerVtx, incomingTrail );
2440           if( modType.equals("resource") || modType.equals("service") ){
2441                   // For an element that is referring to a resource or service model, we replace this
2442               // this element with the "top" element for that resource/service model so that the
2443                   // topology of that resource/service model gets included in this topology.
2444                   // -- Note - since that top element of a service or resource model will point to a widget model, 
2445                   //    we have to track what modelId/version it really maps so we can make our recursive call
2446                   Vertex thisElementsModelVtx = getModelGivenModelVer(thisElementsModelVerVtx, incomingTrail);
2447                   subModelFirstModInvId = thisElementsModelVtx.<String>property("model-invariant-id").orElse(null);
2448                   subModelFirstModVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null);
2449                   
2450                   if( nodeTypeSupportsPersona(thisElementNodeType) ){
2451                                 modInfo4Trail = "," + subModelFirstModInvId + "," + subModelFirstModVerId;
2452                   }
2453                   String modelVerId = thisElementsModelVerVtx.<String>property("model-version-id").orElse(null);
2454                   if( subModelFirstModInvId == null || subModelFirstModInvId.equals("") || subModelFirstModVerId == null || subModelFirstModVerId.equals("") ){
2455                           throw new AAIException("AAI_6132", "Bad Model Definition: Bad model-invariant-id or model-version-id.  Model-ver-id = " + modelVerId);
2456                   }
2457
2458                   elementVtxForThisLevel = getTopElementForSvcOrResModelVer(thisElementsModelVerVtx,  incomingTrail);  
2459                   modConstraintHash2Use = getModConstraintHash( thisLevelElemVtx, modConstraintHash );
2460           }
2461           else {
2462                   elementVtxForThisLevel = thisLevelElemVtx;
2463           }
2464           
2465           if( incomingTrail.equals("") ){
2466                   // This is the first one
2467                   thisGuysTrail = thisElementNodeType + firstElementModelInfo;
2468           }
2469           else {
2470                   thisGuysTrail = incomingTrail + "|" + thisElementNodeType + modInfo4Trail;
2471           }
2472           
2473           // We only want to ensure that a particular element does not repeat on a single "branch".
2474           // It could show up on other branches in the case where it is a sub-model which is being
2475           // used in more than one place.
2476           //
2477           List<String> thisTrailsVidsTraversed = new ArrayList <String>();
2478           thisTrailsVidsTraversed.addAll(vidsTraversed);
2479           thisTrailsVidsTraversed.add(thisElemVid);
2480           
2481           // Look at the elements pointed to at this level and add on their data
2482           Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, elementVtxForThisLevel, "model-element", "constrained-element-set");
2483
2484            while( vertI != null && vertI.hasNext() ){
2485                   Vertex tmpVert = vertI.next();
2486                   String vid = tmpVert.id().toString();
2487                   Map<String,Object> elementHash = new HashMap<String, Object>();
2488                   String connectToType = tmpVert.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2489                   if( connectToType != null && connectToType.equals("model-element") ){
2490                           // A nice, regular old model-element
2491                           elementHash.put(vid, tmpVert);
2492                   }
2493                   else if( (connectToType != null) && connectToType.equals("constrained-element-set") ){
2494                           // translate the constrained-element-set into a hash of model-element Vertex's
2495                           String constrainedElementSetUuid = tmpVert.<String>property("constrained-element-set-uuid").orElse(null);
2496                           if( (modConstraintHash2Use != null) && modConstraintHash2Use.containsKey(constrainedElementSetUuid) ){
2497                                   // This constrained-element-set is being superseded by a different one
2498                                   Vertex replacementConstraintVert = modConstraintHash.get(constrainedElementSetUuid);
2499                                   elementHash = getNextStepElementsFromSet( replacementConstraintVert );
2500                                   // Now that we've found and used the replacement constraint, we don't need to carry it along any farther
2501                                   modConstraintHash.remove(constrainedElementSetUuid);
2502                           }
2503                           else {
2504                                   elementHash = getNextStepElementsFromSet( tmpVert );    
2505                           }
2506                   }
2507                   else {
2508                           String msg = " model element has [connectedTo] edge to improper nodeType= [" 
2509                                           + connectToType + "] trail = [" + incomingTrail + "].";
2510                           throw new AAIException("AAI_6132", msg);
2511                   }
2512                   
2513                   for( Map.Entry<String, Object> entry : elementHash.entrySet() ){
2514                           Vertex elVert = (Vertex)(entry.getValue());
2515                           String tmpElVid = elVert.id().toString();
2516                           String tmpElNT = getModElementWidgetType( elVert, thisGuysTrail );
2517                           String tmpElStepName = getModelElementStepName( elVert, thisGuysTrail);
2518                           
2519                           List<String> linkagePtList = new ArrayList <String>();
2520                           Iterator <VertexProperty<Object>> vpI = elVert.properties("linkage-points");
2521  
2522                           // I am not sure why, but since "linkage-points" is an xml-element-wrapper in the OXM definition, 
2523                           // we get back the whole array of Strings in one String - but still use the "vtx.properties()" to
2524                           // get it - but only look at the first thing returned by the iterator.
2525                           if( vpI.hasNext() ){
2526                                   String tmpLinkageThing = (String)vpI.next().value();
2527                                   linkagePtList = makeSureItsAnArrayList( tmpLinkageThing );
2528                           } 
2529                   
2530                           if( linkagePtList != null && !linkagePtList.isEmpty() ){
2531                                   // This is as far as we can go, we will use the linkage point info to define the 
2532                                   // rest of this "trail" 
2533                                   for( int i = 0; i < linkagePtList.size(); i++ ){
2534                                           Multimap<String, String> tmpMap = collectTopology4LinkagePoint( transId, fromAppId,
2535                                                                 linkagePtList.get(i), thisGuysTrail, currentMap);
2536                                           thisMap.putAll(tmpMap);
2537                                   }
2538                           }
2539                           else {
2540                                   check4EdgeRule(tmpElNT, thisElementNodeType);
2541                                   thisMap.put(thisGuysTrail, tmpElStepName);
2542                                   if( !thisTrailsVidsTraversed.contains(tmpElVid) ){
2543                                           // This is one we would like to use - so we'll recursively get it's result set to add to ours
2544                                           Multimap<String, String> tmpMap = collectTopology4ModelVer( transId, fromAppId,
2545                                                                 elVert, thisGuysTrail, 
2546                                                                 currentMap, thisTrailsVidsTraversed, levelCounter, 
2547                                                                  modConstraintHash2Use, subModelFirstModInvId, subModelFirstModVerId );
2548                                           thisMap.putAll(tmpMap);
2549                                   }
2550                                   else {
2551                                           String modelElementUuid = elVert.<String>property("model-element-uuid").orElse(null);
2552                                           String msg = "Bad Model Definition: looping model-element (model-element-uuid = [" +
2553                                                           modelElementUuid + "]) found trying to add step: [" + tmpElStepName + "], " +
2554                                                           " on trail = [" + thisGuysTrail + "]. ";
2555                                           System.out.println( msg );
2556                                           throw new AAIException("AAI_6132", msg);
2557                                   }       
2558                           }  
2559                   }             
2560           }
2561            
2562           return thisMap;
2563           
2564         } // End of collectTopology4ModelVer()
2565         
2566         
2567         /**
2568          * Check 4 edge rule.
2569          *
2570          * @param nodeTypeA the node type A
2571          * @param nodeTypeB the node type B
2572          * @param loader the db maps
2573          * @throws AAIException the AAI exception
2574          */
2575         public void check4EdgeRule( String nodeTypeA, String nodeTypeB) throws AAIException {
2576                 // Throw an exception if there is no defined edge rule for this combination of nodeTypes in DbEdgeRules.
2577                 
2578                 final EdgeRules edgeRules = EdgeRules.getInstance();
2579                 
2580                 if( !edgeRules.hasEdgeRule(nodeTypeA, nodeTypeB)  
2581                                 &&  !edgeRules.hasEdgeRule(nodeTypeB, nodeTypeA) ){
2582                         // There's no EdgeRule for this -- find out if one of the nodeTypes is invalid or if
2583                         // they are valid, but there's just no edgeRule for them.
2584                         try {
2585                                 loader.introspectorFromName(nodeTypeA);
2586                         } catch (AAIUnknownObjectException e) {
2587                                 String emsg = " Unrecognized nodeType aa [" + nodeTypeA + "]\n";
2588                                 throw new AAIException("AAI_6115", emsg);
2589                         }
2590                         try {
2591                                 loader.introspectorFromName(nodeTypeB);
2592                         } catch (AAIUnknownObjectException e) {
2593                                 String emsg = " Unrecognized nodeType bb [" + nodeTypeB + "]\n";
2594                                 throw new AAIException("AAI_6115", emsg);
2595                         }
2596                         
2597                         String msg = " No Edge Rule found for this pair of nodeTypes (order does not matter) [" 
2598                                           + nodeTypeA + "], [" + nodeTypeB + "].";
2599                         throw new AAIException("AAI_6120", msg);
2600                 }
2601                 
2602                 
2603         }
2604         
2605     
2606         /**
2607          * Collect topology 4 linkage point.
2608          *
2609          * @param transId the trans id
2610          * @param fromAppId the from app id
2611          * @param linkagePointStr -- Note it is in reverse order from where we connect to it.
2612          * @param incomingTrail -- trail of nodeTypes that got us here (this vertex) from the top
2613          * @param currentMap the current map -- that got us to this point (that we will use as the base of the map we will return)
2614          * @param loader the db maps
2615          * @return Map of the topology
2616          * @throws AAIException the AAI exception
2617          */
2618         public Multimap<String, String> collectTopology4LinkagePoint(String transId, String fromAppId,
2619                                                                  String linkagePointStrVal, String incomingTrail, Multimap<String,String> currentMap)
2620                                   throws AAIException {
2621
2622           Multimap<String, String> thisMap = ArrayListMultimap.create();
2623           thisMap.putAll(currentMap);
2624           String thisGuysTrail = incomingTrail;
2625           
2626           // NOTE - "trails" can have multiple parts now since we track persona info for some.
2627           //      We just want to look at the node type info - which would be the piece
2628           //      before any commas (if there are any).
2629          
2630           String [] trailSteps = thisGuysTrail.split("\\|");
2631           if( trailSteps == null || trailSteps.length == 0 ){
2632                   throw new AAIException("AAI_6125", "Bad incomingTrail passed to collectTopology4LinkagePoint(): [" + incomingTrail + "] ");
2633           }
2634           String lastStepString = trailSteps[trailSteps.length - 1];
2635           String [] stepPieces = lastStepString.split(",");
2636           String lastStepNT = stepPieces[0];
2637           
2638           // It is assumed that the linkagePoint string will be a pipe-delimited string where each
2639           // piece is an AAIProperties.NODE_TYPE.  For now, the first thing to connect to is what is on the farthest right.
2640           // Example:  linkagePoint =  "pserver|p-interface|l-interface"   would mean that we're connecting to the l-interface
2641           //      but that after that, we connect to a p-interface followed by a pserver.
2642           // It might have been more clear to define it in the other direction, but for now, that is it. (16-07)
2643           String linkagePointStr = linkagePointStrVal;
2644          
2645           // We are getting these with more than linkage thing in one string.  
2646           //   Ie. "pserver|lag-interface|l-interface, pserver|p-interface|l-interface, vlan|l-interface"
2647           linkagePointStr = linkagePointStr.replace("[",  "");
2648           linkagePointStr = linkagePointStr.replace("]",  "");
2649           linkagePointStr = linkagePointStr.replace(" ",  "");
2650           
2651           String [] linkage = linkagePointStr.split("\\,");
2652           for( int x = 0; x < linkage.length; x++ ){
2653                   lastStepNT = stepPieces[0]; 
2654                   String thisStepNT = "";
2655                   String [] linkageSteps = linkage[x].split("\\|");
2656                   if( linkageSteps == null || linkageSteps.length == 0 ){
2657                           throw new AAIException("AAI_6125", "Bad linkagePointStr passed to collectTopology4LinkagePoint(): [" + linkagePointStr + "] ");
2658                   }
2659                   for( int i=(linkageSteps.length - 1); i >= 0; i-- ){
2660                           thisStepNT = linkageSteps[i];
2661                           check4EdgeRule(lastStepNT, thisStepNT);
2662                           thisMap.put(thisGuysTrail, thisStepNT);
2663                           thisGuysTrail = thisGuysTrail + "|" + thisStepNT;
2664                           lastStepNT = thisStepNT;
2665                   }
2666           }
2667           return thisMap;
2668           
2669         } // End of collectTopology4LinkagePoint()
2670         
2671    
2672         /**
2673          * Gets the next step elements from set.
2674          *
2675          * @param constrElemSetVtx the constr elem set vtx
2676          * @return Hash of the set of model-elements this set represents
2677          * @throws AAIException the AAI exception
2678          */
2679         public Map<String,Object> getNextStepElementsFromSet( Vertex constrElemSetVtx )
2680                                   throws AAIException {
2681                 // Take a constrained-element-set and figure out the total set of all the possible elements that it
2682                 // represents and return them as a Hash.
2683           
2684                 Map<String,Object> retElementHash = new HashMap<String, Object>();
2685         
2686                 if( constrElemSetVtx == null ){
2687                           String msg = " getNextStepElementsFromSet() called with null constrElemSetVtx ";
2688                           throw new AAIException("AAI_6125", msg);
2689                 }
2690         
2691                 String constrNodeType = constrElemSetVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2692                 String constrElemSetUuid = constrElemSetVtx.<String>property("constrained-element-set-uuid").orElse(null);
2693                 if( constrNodeType == null || !constrNodeType.equals("constrained-element-set") ){
2694                           String msg = " getNextStepElementsFromSet() called with wrong type model: [" + constrNodeType + "]. ";
2695                           throw new AAIException("AAI_6125", msg);
2696                 }
2697                 
2698                 ArrayList <Vertex>  choiceSetVertArray = new ArrayList<Vertex>();
2699                 Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, constrElemSetVtx, "element-choice-set");
2700                 int setCount = 0;
2701                 while( vertI != null && vertI.hasNext() ){
2702                         Vertex choiceSetVertex = vertI.next();
2703                         String constrSetType = choiceSetVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2704                         if( constrSetType != null && constrSetType.equals("element-choice-set") ){
2705                                 choiceSetVertArray.add(choiceSetVertex);
2706                                 setCount++;
2707                         }
2708                 }
2709                         
2710                 if( setCount == 0 ){
2711                           String msg = "No element-choice-set found under constrained-element-set-uuid = " + constrElemSetUuid;
2712                           throw new AAIException("AAI_6132", msg);
2713                 }
2714                 
2715                 // Loop through each choice-set and grab the model-elements
2716                 for( int i = 0; i < setCount; i++ ){
2717                         Vertex choiceSetVert = choiceSetVertArray.get(i);
2718                         Iterator<Vertex> mVertI = this.traverseIncidentEdges(EdgeType.TREE, choiceSetVert, "model-element");
2719                         int elCount = 0;
2720                         while( mVertI != null && mVertI.hasNext() ){
2721                                 Vertex tmpElVertex = mVertI.next();
2722                                 String elNodeType = tmpElVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2723                                 if( elNodeType != null && elNodeType.equals("model-element") ){
2724                                         String tmpVid = tmpElVertex.id().toString();
2725                                         retElementHash.put(tmpVid, tmpElVertex);
2726                                         elCount++;
2727                                 }
2728                                 else {
2729                                         // unsupported node type found for this choice-set
2730                                         String msg = "Unsupported nodeType (" + elNodeType 
2731                                                         + ") found under choice-set under constrained-element-set-uuid = " + constrElemSetUuid;
2732                                         throw new AAIException("AAI_6132", msg);
2733                                 }
2734                         }
2735                                 
2736                         if( elCount == 0 ){
2737                                   String msg = "No model-elements found in choice-set under constrained-element-set-uuid = " + constrElemSetUuid;
2738                                   throw new AAIException("AAI_6132", msg);
2739                         }
2740                         
2741                 }
2742           return retElementHash;
2743           
2744         } // End of getNextStepElementsFromSet()
2745         
2746         
2747         
2748         /**
2749          * Gen topo map 4 named Q.
2750          *
2751          * @param transId the trans id
2752          * @param fromAppId the from app id
2753          * @param queryVertex the query vertex
2754          * @param namedQueryUuid the named query uuid
2755          * @return MultiMap of valid next steps for each potential query-element
2756          * @throws AAIException the AAI exception
2757          */
2758         public Multimap<String, String> genTopoMap4NamedQ(String transId, String fromAppId,
2759                                                       Vertex queryVertex, String namedQueryUuid )
2760                                   throws AAIException {
2761           
2762           if( queryVertex == null ){
2763                   throw new AAIException("AAI_6125", "null queryVertex passed to genTopoMap4NamedQ()");
2764           }
2765           
2766           Multimap<String, String> initialEmptyMap = ArrayListMultimap.create();
2767           List<String> vidsTraversed = new ArrayList<>();
2768           
2769           Vertex firstElementVertex = null;
2770           Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, queryVertex, "named-query-element");
2771           int elCount = 0;
2772           while( vertI != null && vertI.hasNext() ){
2773                   elCount++;
2774                   firstElementVertex = vertI.next();
2775           }
2776                 
2777           if( elCount > 1 ){
2778                   throw new AAIException("AAI_6133", "Illegal query defined: More than one first element defined for = " + namedQueryUuid);
2779           }
2780           
2781           if( firstElementVertex == null ){
2782                   throw new AAIException("AAI_6114", "Could not find first query element = " + namedQueryUuid);
2783           }
2784         
2785           Vertex modVtx = getModelThatNqElementRepresents( firstElementVertex, "" );
2786           String modelType = getModelTypeFromModel( modVtx, "" );         
2787           if( ! modelType.equals("widget") ){
2788                   throw new AAIException("AAI_6133", "Bad Named Query Definition: First element must correspond to a widget type model.  Named Query UUID = "
2789                                   + namedQueryUuid);
2790           }
2791           
2792           Multimap<String, String> collectedMap = collectTopology4NamedQ( transId, fromAppId,
2793                                 firstElementVertex, "", 
2794                                 initialEmptyMap, vidsTraversed, 0);
2795           
2796           return collectedMap;
2797           
2798         } // End of genTopoMap4NamedQ()
2799
2800         
2801     
2802         /**
2803          * Collect topology 4 named Q.
2804          *
2805          * @param transId the trans id
2806          * @param fromAppId the from app id
2807          * @param thisLevelElemVtx the model element vertex for this level
2808          * @param levelCounter the level counter
2809          * @return resultSet
2810          * @throws AAIException the AAI exception
2811          */
2812         public Multimap<String, String> collectTopology4NamedQ(String transId, String fromAppId,
2813                                                            Vertex thisLevelElemVtx, String incomingTrail,
2814                                                            Multimap<String,String> currentMap, List<String> vidsTraversed, int levelCounter )
2815                                   throws AAIException {
2816         
2817           levelCounter++;
2818
2819           Multimap<String, String> thisMap = ArrayListMultimap.create();
2820           thisMap.putAll(currentMap);
2821          
2822           String thisElemVid = thisLevelElemVtx.id().toString();
2823           if( levelCounter > MAX_LEVELS ) {
2824                   throw new AAIException("AAI_6125", "collectModelStructure() has looped across more levels than allowed: " + MAX_LEVELS + ". ");
2825           }
2826           String thisGuysTrail = "";
2827           
2828           // find out what widget (and thereby what aai-node-type) this element represents
2829           String thisElementNodeType = getNqElementWidgetType( transId, fromAppId, thisLevelElemVtx, incomingTrail );
2830           
2831           if( incomingTrail.equals("") ){
2832                   // This is the first one
2833                   thisGuysTrail = thisElementNodeType;
2834           }
2835           else {
2836                   thisGuysTrail = incomingTrail + "|" + thisElementNodeType;
2837           }
2838           
2839           vidsTraversed.add(thisElemVid);
2840           
2841           // Look at the elements pointed to at this level and add on their data
2842           Iterator<Vertex> vertI = this.traverseIncidentEdges(EdgeType.TREE, thisLevelElemVtx, "named-query-element");
2843           while( vertI != null && vertI.hasNext() ){
2844                   Vertex tmpVert = vertI.next();
2845                   String tmpVid = tmpVert.id().toString();
2846                   String tmpElNT = getNqElementWidgetType( transId, fromAppId, tmpVert, thisGuysTrail );
2847                   thisMap.put(thisGuysTrail, tmpElNT);
2848                   if( !vidsTraversed.contains(tmpVid) ){
2849                           // This is one we would like to use - so we'll recursively get it's result set to add to ours
2850                           Multimap<String, String> tmpMap = collectTopology4NamedQ( transId, fromAppId,
2851                                                 tmpVert, thisGuysTrail, 
2852                                                 currentMap, vidsTraversed, levelCounter);
2853                           thisMap.putAll(tmpMap);
2854                   }
2855           }
2856           
2857           return thisMap;
2858           
2859         } // End of collectTopology4NamedQ()
2860
2861
2862         /**
2863          * Gets the model that NamedQuery element represents.
2864          *
2865          * @param elementVtx the NQ element vtx
2866          * @param elementTrail the element trail
2867          * @return the model that element represents
2868          * @throws AAIException the AAI exception
2869          */
2870         public Vertex getModelThatNqElementRepresents(Vertex elementVtx, String elementTrail )
2871                 throws AAIException {
2872                 
2873                   // Get the model that a named-query element represents
2874                   Vertex modVtx = null;
2875                   Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.COUSIN, elementVtx, "model");
2876                   int modCount = 0;
2877                   while( mvertI != null && mvertI.hasNext() ){
2878                           modCount++;
2879                           modVtx = mvertI.next();
2880                   }
2881                         
2882                   if( modCount > 1 ){
2883                           String msg = "Illegal element defined: More than one model pointed to by a single named-query-element at [" +
2884                                           elementTrail + "].";
2885                           throw new AAIException("AAI_6125", msg);
2886                   }
2887                   
2888                   if( modVtx == null ){
2889                           String msg = "Bad named-query definition: Could not find model for element. ";
2890                           if( !elementTrail.equals("") ){
2891                                   msg = "Bad named-query definition: Could not find model for named-query-element at [" + elementTrail + "].";
2892                           }
2893                           throw new AAIException("AAI_6132", msg);
2894                   }
2895                   
2896                   String nodeType = modVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2897                   if( (nodeType != null) && nodeType.equals("model") ){
2898                           return modVtx;
2899                   }
2900                   else {
2901                           String msg = "Illegal Named Query element defined: expecting a 'model', but found 'isA' edge pointing to nodeType = " + 
2902                                           nodeType + "] at [" +   elementTrail + "].";
2903                           throw new AAIException("AAI_6125", msg);
2904                   }       
2905                     
2906         }// getModelThatNqElementRepresents()
2907         
2908         
2909         /**
2910          * Gets the model-ver that element represents.
2911          *
2912          * @param elementVtx the element vtx
2913          * @param elementTrail the element trail
2914          * @return the model-ver that element represents
2915          * @throws AAIException the AAI exception
2916          */
2917         public Vertex getModelVerThatElementRepresents(Vertex elementVtx, String elementTrail )
2918                 throws AAIException {
2919                 
2920                   // Get the model-ver that an element represents
2921                   Vertex modVerVtx = null;
2922                   Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.COUSIN, elementVtx, "model-ver");
2923                   int modCount = 0;
2924                   while( mvertI != null && mvertI.hasNext() ){
2925                           modCount++;
2926                           modVerVtx = mvertI.next();
2927                   }
2928                         
2929                   if( modCount > 1 ){
2930                           String msg = "Illegal element defined: More than one model pointed to by a single element at [" +
2931                                           elementTrail + "].";
2932                           throw new AAIException("AAI_6125", msg);
2933                   }
2934                   
2935                   if( modVerVtx == null ){
2936                           String msg = "Bad model definition: Could not find model-ver for model-element. ";
2937                           if( !elementTrail.equals("") ){
2938                                   msg = "Bad model definition: Could not find model-VER for model-element at [" + elementTrail + "].";
2939                           }
2940                           throw new AAIException("AAI_6132", msg);
2941                   }
2942                   
2943                   String nodeType = modVerVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);
2944                   if( (nodeType != null) && nodeType.equals("model-ver") ){
2945                           return modVerVtx;
2946                   }
2947                   else {
2948                           String msg = "Illegal model-element defined: expecting a 'model-ver', but found 'isA' edge pointing to nodeType = " + 
2949                                           nodeType + "] at [" +   elementTrail + "].";
2950                           throw new AAIException("AAI_6125", msg);
2951                   }
2952                     
2953         }// getModelVerThatElementRepresents()
2954         
2955         
2956         
2957         /**
2958          * Gets the model that is parent to model-ver node.
2959          *
2960          * @param modVerVtx the model-ver vtx
2961          * @param elementTrail the element trail
2962          * @return the model that element represents
2963          * @throws AAIException the AAI exception
2964          */
2965         public Vertex getModelGivenModelVer(Vertex modVerVtx, String elementTrail )
2966                 throws AAIException {
2967                 
2968                 // Get the parent model for this "model-ver" node
2969                   Vertex modVtx = null;
2970                   Iterator<Vertex> mvertI = this.traverseIncidentEdges(EdgeType.TREE, modVerVtx, "model");
2971                   int modCount = 0;
2972                   while( mvertI != null && mvertI.hasNext() ){
2973                           modCount++;
2974                           modVtx = mvertI.next();
2975                   }
2976                         
2977                   if( modCount > 1 ){
2978                           String msg = "Illegal model-ver node defined: More than one model points to it with a 'has' edge [" +
2979                                           elementTrail + "].";
2980                           throw new AAIException("AAI_6125", msg);
2981                   }
2982                   
2983                   if( modVtx == null ){
2984                           String msg = "Bad model-ver node: Could not find parent model. ";
2985                           if( !elementTrail.equals("") ){
2986                                   msg = "Bad model-ver node: Could not find parent model. [" + elementTrail + "].";
2987                           }
2988                           throw new AAIException("AAI_6132", msg);
2989                   }
2990                   
2991                   String nodeType = modVtx.<String>property(AAIProperties.NODE_TYPE).orElse(null);;
2992                   if( (nodeType != null) && nodeType.equals("model") ){
2993                           // Found what we were looking for.
2994                           return modVtx;
2995                   }
2996                   else {
2997                           // Something is amiss
2998                           String msg = " Could not find parent model node for model-ver node at [" +
2999                                           elementTrail + "].";
3000                           throw new AAIException("AAI_6125", msg);
3001                   }
3002                     
3003                 
3004         }// getModelGivenModelVer()
3005         
3006         
3007                         
3008         /**
3009          * Gets the model type.
3010          *
3011          * @param modelVtx the model vtx
3012          * @param elementTrail the element trail
3013          * @return the model type
3014          * @throws AAIException the AAI exception
3015          */
3016         public String getModelTypeFromModel(Vertex modelVtx, String elementTrail )
3017                 throws AAIException {
3018                   
3019                 // Get the model-type from a model vertex
3020                 if( modelVtx == null ){
3021                          String msg = " null modelVtx passed to getModelTypeFromModel() ";
3022                          throw new AAIException("AAI_6114", msg);
3023                 }                 
3024                  
3025                 String modelType = modelVtx.<String>property("model-type").orElse(null);
3026                 if( (modelType == null) || modelType.equals("") ){
3027                         String msg = "Could not find model-type for model encountered at [" + elementTrail + "].";
3028                         throw new AAIException("AAI_6132", msg);
3029                 }
3030                 
3031                 if( !modelType.equals("widget") && !modelType.equals("resource") && !modelType.equals("service") ){
3032                         String msg = "Unrecognized model-type, [" + modelType + "] for model pointed to by element at [" + elementTrail + "].";
3033                         throw new AAIException("AAI_6132", msg);
3034                 }
3035                   
3036                 return modelType; 
3037                 
3038         }// getModelTypeFromModel()
3039         
3040         
3041                 
3042         /**
3043         * Gets the model type given model-ver
3044         *
3045         * @param modelVerVtx the model-ver vtx
3046         * @param elementTrail the element trail
3047         * @return the model type
3048         * @throws AAIException the AAI exception
3049         */
3050         public String getModelTypeFromModelVer(Vertex modelVerVtx, String elementTrail )
3051         throws AAIException {
3052           
3053         // Get the model-type given a model-ver vertex
3054         if( modelVerVtx == null ){
3055                  String msg = " null modelVerVtx passed to getModelTypeFromModelVer() ";
3056                  throw new AAIException("AAI_6114", msg);
3057         }                 
3058          
3059         Vertex modVtx = getModelGivenModelVer( modelVerVtx, elementTrail );
3060         String modelType = modVtx.<String>property("model-type").orElse(null);
3061         if( (modelType == null) || modelType.equals("") ){
3062                 String msg = "Could not find model-type for model encountered at [" + elementTrail + "].";
3063                 throw new AAIException("AAI_6132", msg);
3064         }
3065         
3066         if( !modelType.equals("widget") && !modelType.equals("resource") && !modelType.equals("service") ){
3067                 String msg = "Unrecognized model-type, [" + modelType + "] for model pointed to by element at [" + elementTrail + "].";
3068                 throw new AAIException("AAI_6132", msg);
3069         }
3070           
3071         return modelType; 
3072         
3073         }// getModelTypeFromModelVer()
3074                 
3075                 
3076
3077         /**
3078          * Gets the model-element step name.
3079          *
3080          * @param elementVtx the model-element vtx
3081          * @param elementTrail the element trail
3082          * @param loader the db maps
3083          * @return the element step name
3084          * @throws AAIException the AAI exception
3085          */
3086         public String getModelElementStepName(Vertex elementVtx, String elementTrail)
3087                 throws AAIException {
3088                 
3089                 // Get the "step name"  for a model-element 
3090                 // Step names look like this for widget-models:   AAIProperties.NODE_TYPE
3091                 // Step names look like this for resource/service models: "aai-node-type,model-invariant-id,model-version-id"
3092                 // NOTE -- if the element points to a resource or service model, then we'll return the
3093                 //        widget-type of the first element (crown widget) for that model.
3094                 String thisElementNodeType = "?";
3095                 Vertex modVerVtx = getModelVerThatElementRepresents( elementVtx, elementTrail );
3096                 String modelType = getModelTypeFromModelVer( modVerVtx, elementTrail );
3097                 
3098                 if( modelType == null ){
3099                         String msg = " could not determine modelType in getModelElementStepName().  elementTrail = [" + elementTrail + "].";
3100                         throw new AAIException("AAI_6132", msg);
3101                 }
3102                   
3103                 if( modelType.equals("widget") ){
3104                         // NOTE: for models that have model-type = "widget", their "model-name" maps directly to aai-node-type 
3105                         thisElementNodeType = modVerVtx.<String>property("model-name").orElse(null);
3106                         if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){
3107                                 String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "].";
3108                                 throw new AAIException("AAI_6132", msg);
3109                         }
3110                         return thisElementNodeType;
3111                 }
3112                 else if( modelType.equals("resource") || modelType.equals("service") ){
3113                         Vertex modVtx = getModelGivenModelVer( modVerVtx, elementTrail );
3114                         String modInvId = modVtx.<String>property("model-invariant-id").orElse(null); 
3115                         String modVerId = modVerVtx.<String>property("model-version-id").orElse(null);
3116                         Vertex relatedTopElementModelVtx = getTopElementForSvcOrResModelVer( modVerVtx, elementTrail );
3117                         Vertex relatedModelVtx = getModelVerThatElementRepresents( relatedTopElementModelVtx, elementTrail );
3118                         thisElementNodeType = relatedModelVtx.<String>property("model-name").orElse(null);
3119                         
3120                         if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){
3121                                 String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "].";
3122                                 throw new AAIException("AAI_6132", msg);
3123                         }
3124                         
3125                         String stepName = "";
3126                         if( nodeTypeSupportsPersona(thisElementNodeType) ){
3127                                 // This nodeType that this resource or service model refers to does support persona-related fields, so
3128                                 // we will use model-invariant-id and model-version-id as part of the step name.
3129                                 stepName = thisElementNodeType + "," + modInvId + "," + modVerId;
3130                         }
3131                         else {
3132                                 stepName = thisElementNodeType;
3133                         }
3134                         return stepName;
3135                 }
3136                 else {
3137                         String msg = " Unrecognized model-type = [" + modelType + "] pointed to by element at [" + elementTrail + "].";
3138                         throw new AAIException("AAI_6132", msg);
3139                 }
3140                   
3141         }// getModelElementStepName()
3142         
3143         
3144         
3145         /**
3146          * Node type supports persona.
3147          *
3148          * @param nodeType the node type
3149          * @param loader the db maps
3150          * @return the boolean
3151          * @throws AAIException the AAI exception
3152          */
3153         public Boolean nodeTypeSupportsPersona(String nodeType)
3154                         throws AAIException {
3155                 
3156                 if( nodeType == null || nodeType.equals("") ){
3157                         return false;
3158                 }
3159                 Introspector obj = null;
3160                 try {
3161                         obj = loader.introspectorFromName(nodeType);
3162                 } catch (AAIUnknownObjectException e) {
3163                         String emsg = " Unrecognized nodeType [" + nodeType + "]\n";
3164                         throw new AAIException("AAI_6115", emsg);
3165                 }
3166                 
3167                 Collection <String> props4ThisNT = loader.introspectorFromName(nodeType).getProperties();
3168                 if( !props4ThisNT.contains(addDBAliasedSuffix("model-invariant-id")) || !props4ThisNT.contains(addDBAliasedSuffix("model-version-id")) ){
3169                         return false;
3170                 }
3171                 else {
3172                         return true;
3173                 }
3174                 
3175         }// nodeTypeSupportsPersona()
3176         
3177         
3178         /**
3179          * Gets a Named Query element's widget type.
3180          *
3181          * @param elementVtx the named-query element vtx
3182          * @param elementTrail the element trail
3183          * @return the element widget type
3184          * @throws AAIException the AAI exception
3185          */
3186         public String getNqElementWidgetType(String transId, String fromAppId,
3187                                          Vertex elementVtx, String elementTrail )
3188                 throws AAIException {
3189                 
3190                 String thisNqElementWidgetType = "";
3191                 // Get the associated node-type for the model pointed to by a named-query-element.
3192                 // NOTE -- if the element points to a resource or service model, then we'll return the
3193                 //        widget-type of the first element (crown widget) for that model.
3194                 Vertex modVtx = getModelThatNqElementRepresents( elementVtx, elementTrail );
3195                 String modelType = getModelTypeFromModel( modVtx, elementTrail );
3196                 
3197                 if( modelType == null || !modelType.equals("widget") ){
3198                         String emsg = " Model Type must be 'widget' for NamedQuery elements.  Found [" + modelType + "] at [" + 
3199                                         elementTrail + "]\n";
3200                         throw new AAIException("AAI_6132", emsg);
3201                 }
3202                 else {
3203                         // For a Widget model, the nodeType is just mapped to the model-element.model-name
3204                         List<Vertex> modVerVtxArr =  getModVersUsingModel(transId, fromAppId, modVtx);
3205                         if( modVerVtxArr != null && !modVerVtxArr.isEmpty() ){
3206                                 thisNqElementWidgetType = (modVerVtxArr.get(0)).<String>property("model-name").orElse(null);
3207                         }
3208                         if( thisNqElementWidgetType == null || thisNqElementWidgetType.equals("") ){
3209                                 String emsg = " Widget type could not be determined at [" + elementTrail + "]\n";
3210                                         throw new AAIException("AAI_6132", emsg);
3211                         }
3212                         else {
3213                                 return thisNqElementWidgetType;
3214                         }
3215                 }
3216                 
3217                 
3218         }// End  getNqElementWidgetType()
3219         
3220         
3221         /**
3222          * Gets a model-element's top widget type.
3223          *
3224          * @param elementVtx the model element vtx
3225          * @param elementTrail the element trail
3226          * @return the element widget type
3227          * @throws AAIException the AAI exception
3228          */
3229         public String getModElementWidgetType(Vertex elementVtx, String elementTrail )
3230                 throws AAIException {
3231                 
3232                 // Get the associated node-type for the model-ver pointed to by a model-element.
3233                 // NOTE -- if the element points to a resource or service model, then we'll return the
3234                 //        widget-type of the first element (crown widget) for that model.
3235                 Vertex modVerVtx = getModelVerThatElementRepresents( elementVtx, elementTrail );
3236                 String thisElementNodeType = getModelVerTopWidgetType( modVerVtx, elementTrail );
3237                 return thisElementNodeType;
3238                         
3239         }// End  getModElementWidgetType()
3240         
3241         
3242         /**
3243          * Gets the node using unique identifier
3244          *
3245          * @param transId the trans id
3246          * @param fromAppId the from app id
3247          * @param nodeType the nodeType 
3248          * @param idPropertyName the property name of the unique identifier
3249          * @param uniqueIdVal the UUID value
3250          * @return unique vertex found using UUID
3251          * @throws AAIException the AAI exception
3252          */
3253         public Vertex getNodeUsingUniqueId(String transId, String fromAppId,
3254                                        String nodeType, String idPropertyName, String uniqueIdVal )
3255                 throws AAIException {
3256                 
3257                 // Given a unique identifier, get the Vertex 
3258                 if( uniqueIdVal == null || uniqueIdVal.equals("")  ){
3259                         String emsg = " Bad uniqueIdVal passed to getNodeUsingUniqueId(): [" 
3260                                         + uniqueIdVal + "]\n";
3261                         throw new AAIException("AAI_6118", emsg);
3262                 }
3263                 
3264                 if( idPropertyName == null || idPropertyName.equals("")  ){
3265                         String emsg = " Bad idPropertyName passed to getNodeUsingUniqueId(): [" 
3266                                         + idPropertyName + "]\n";
3267                         throw new AAIException("AAI_6118", emsg);
3268                 }               
3269                 
3270                 if( nodeType == null || nodeType.equals("")  ){
3271                         String emsg = " Bad nodeType passed to getNodeUsingUniqueId(): [" 
3272                                         + nodeType + "]\n";
3273                         throw new AAIException("AAI_6118", emsg);
3274                 }               
3275                 
3276                 Vertex uniqVtx = null;
3277                 Iterable <?> uniqVerts = null;
3278                 uniqVerts = engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,nodeType).has(idPropertyName,uniqueIdVal).toList();
3279                 if( uniqVerts == null ){
3280                         String emsg = "Node could not be found for nodeType = [" + nodeType 
3281                                         + "], propertyName = [" + idPropertyName  
3282                                         + "], propertyValue = [" + uniqueIdVal  + "]\n";
3283                         throw new AAIException("AAI_6114", emsg);
3284                 }
3285                 else { 
3286                         int count = 0;
3287                         Iterator <?> uniqVertsIter = uniqVerts.iterator();
3288                         if( !uniqVertsIter.hasNext() ){
3289                                 String emsg = "Node could not be found for nodeType = [" + nodeType 
3290                                                 + "], propertyName = [" + idPropertyName  
3291                                                 + "], propertyValue = [" + uniqueIdVal  + "]\n";
3292                                 throw new AAIException("AAI_6114", emsg);
3293                         }
3294                         else {
3295                                 while( uniqVertsIter.hasNext() ){
3296                                         count++;
3297                                         uniqVtx = (Vertex) uniqVertsIter.next();
3298                                         if( count > 1 ){
3299                                                 String emsg = "More than one node found for nodeType = [" + nodeType 
3300                                                                 + "], propertyName = [" + idPropertyName  
3301                                                                 + "], propertyValue = [" + uniqueIdVal  + "]\n";
3302                                                 throw new AAIException("AAI_6132", emsg);
3303                                         }
3304                                 }
3305                         }
3306                 }
3307                 
3308                 return uniqVtx;
3309         }// End getNodeUsingUniqueId()
3310         
3311         
3312         /**
3313          * Gets the model-ver nodes using name.
3314          *
3315          * @param transId the trans id
3316          * @param fromAppId the from app id
3317          * @param modelName the model name
3318          * @return the model-ver's that use this name
3319          * @throws AAIException the AAI exception
3320          */
3321         public List<Vertex> getModelVersUsingName(String transId, String fromAppId,
3322                                               String modelName )
3323                 throws AAIException {
3324                 
3325                 // Given a "model-name", find the model-ver vertices that this maps to
3326                 if( modelName == null || modelName.equals("")  ){
3327                         String emsg = " Bad modelName passed to getModelVersUsingName(): [" 
3328                                         + modelName + "]\n";
3329                         throw new AAIException("AAI_6118", emsg);
3330                 }
3331                 
3332                 List<Vertex> retVtxArr = new ArrayList<>();
3333                 Iterator<Vertex> modVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName);
3334                 if( !modVertsIter.hasNext() ){
3335                         String emsg = "Model-ver record(s) could not be found for model-ver data passed.  model-name = [" + 
3336                                         modelName + "]\n";
3337                         throw new AAIException("AAI_6132", emsg);
3338                 }
3339                 else { 
3340                         while( modVertsIter.hasNext() ){
3341                                 Vertex tmpModelVerVtx = (Vertex) modVertsIter.next();
3342                                 retVtxArr.add(tmpModelVerVtx);
3343                         }
3344                 }
3345                 
3346                 return retVtxArr;
3347                 
3348         }// End getModelVersUsingName()
3349         
3350         
3351         /**
3352          * Gets the model-ver nodes using model-invariant-id.
3353          *
3354          * @param transId the trans id
3355          * @param fromAppId the from app id
3356          * @param model-invariant-id (uniquely identifies a model)
3357          * @return the model-ver's defined for the corresponding model 
3358          * @throws AAIException the AAI exception
3359          */
3360         public Iterator<Vertex> getModVersUsingModelInvId(String transId, String fromAppId,
3361                                                       String modelInvId )
3362                 throws AAIException {
3363                 
3364                 // Given a "model-invariant-id", find the model-ver nodes that this maps to
3365                 if( modelInvId == null || modelInvId.equals("")  ){
3366                         String emsg = " Bad model-invariant-id passed to getModVersUsingModelInvId(): [" 
3367                                         + modelInvId + "]\n";
3368                         throw new AAIException("AAI_6118", emsg);
3369                 }
3370                 
3371                 Vertex modVtx = getNodeUsingUniqueId(transId, fromAppId, "model", "model-invariant-id", modelInvId);
3372                 List<Vertex> retVtxArr =  getModVersUsingModel(transId, fromAppId, modVtx);
3373                 if( retVtxArr == null || retVtxArr.isEmpty() ){
3374                         String emsg = " Model-ver record(s) could not be found attached to model with model-invariant-id = [" + 
3375                                         modelInvId + "]\n";
3376                         throw new AAIException("AAI_6132", emsg);
3377                 }
3378                 
3379                 return retVtxArr.iterator();
3380         }// End getModVersUsingModelInvId()
3381         
3382         
3383         /**
3384          * Gets the model-ver nodes using a model node.
3385          *
3386          * @param transId the trans id
3387          * @param fromAppId the from app id
3388          * @param  model vertex
3389          * @return the model-ver's defined for the corresponding model 
3390          * @throws AAIException the AAI exception
3391          */
3392         public List<Vertex> getModVersUsingModel(String transId, String fromAppId,
3393                                              Vertex modVtx )
3394                 throws AAIException {
3395                 
3396                 if( modVtx == null ){
3397                         String emsg = " Null model vertex passed to getModVersUsingModel(): ";
3398                         throw new AAIException("AAI_6118", emsg);
3399                 }
3400                 
3401                 List<Vertex> retVtxArr = new ArrayList<>();
3402                 Iterator<Vertex> modVerVertsIter = this.traverseIncidentEdges(EdgeType.TREE, modVtx, "model-ver");
3403                 if(!modVerVertsIter.hasNext()){
3404                         String modelInvId = modVtx.<String>property("model-invariant-id").orElse(null);
3405                         String emsg = "Model-ver record(s) could not be found attached to model with model-invariant-id = [" + 
3406                                         modelInvId + "]\n";
3407                         throw new AAIException("AAI_6132", emsg);
3408                 }
3409                 else { 
3410                         while( modVerVertsIter.hasNext() ){
3411                                 Vertex tmpModelVtx = (Vertex) modVerVertsIter.next();
3412                                 retVtxArr.add(tmpModelVtx);
3413                         }
3414                 }
3415                 
3416                 return retVtxArr;
3417                 
3418         }// End getModVersUsingModel()
3419         
3420         /**
3421          * Gets the model-version-ids using model-name.
3422          *
3423          * @param transId the trans id
3424          * @param fromAppId the from app id
3425          * @param modelName the model name
3426          * @return the model uuids using name
3427          * @throws AAIException the AAI exception
3428          */
3429         public List<String> getModelVerIdsUsingName( String transId, String fromAppId,
3430                         String modelName )
3431                 throws AAIException {
3432                 
3433                 // Given a model-name find the model-ver nodes that it maps to
3434                 if( modelName == null || modelName.equals("")  ){
3435                         String emsg = " Bad modelName passed to getModelVerIdsUsingName(): [" 
3436                                         + modelName + "]\n";
3437                         throw new AAIException("AAI_6118", emsg);
3438                 }
3439                 
3440                 List<String> retArr = new ArrayList<>();
3441                 Iterator<Vertex> modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName);
3442                 if( !modVerVertsIter.hasNext() ){
3443                         String emsg = " model-ver record(s) could not be found for model data passed.  model-name = [" + 
3444                                         modelName + "]\n";
3445                         throw new AAIException("AAI_6114", emsg);
3446                 }
3447                 else { 
3448                         while( modVerVertsIter.hasNext() ){
3449                                 Vertex modelVerVtx = (Vertex) modVerVertsIter.next();
3450                                 String tmpUuid = modelVerVtx.<String>property("model-version-id").orElse(null);
3451                                 if( (tmpUuid != null) && !tmpUuid.equals("") && !retArr.contains(tmpUuid) ){
3452                                         retArr.add(tmpUuid);
3453                                 }
3454                         }
3455                 }
3456                 
3457                 if( retArr.isEmpty() ){
3458                         String emsg = "No model-ver record found for model-name = [" 
3459                                         + modelName + "]\n";
3460                         throw new AAIException("AAI_6132", emsg);
3461                 }
3462                 
3463                 return retArr;
3464         }// End getModelVerIdsUsingName()
3465         
3466         
3467         /**
3468          * Gets the model top widget type.
3469          *
3470          * @param transId the trans id
3471          * @param fromAppId the from app id
3472          * @param modelVersionId the model-version-id
3473          * @param modelInvId the model-invariant-id
3474          * @param modelName the model-name
3475          * @return the model top widget type
3476          * @throws AAIException the AAI exception
3477          */
3478         public String getModelVerTopWidgetType( String transId, String fromAppId,
3479                         String modelVersionId, String modelInvId, String modelName )
3480                 throws AAIException {
3481                 
3482                 // Could be given a model-ver's key info (model-version-id), OR, just a (non-unique) model-name,
3483                 //     Or just a model-invariant-id (which could have multiple model-ver records under it).
3484                 //     In any case, they should only map to one single "top" node-type for the first element. 
3485                 
3486                 String nodeType = "?";
3487                 Iterator<Vertex> modVerVertsIter;
3488                 
3489                 if( modelVersionId != null && !modelVersionId.equals("") ){
3490                         // this would be the best - we can just look up the model-ver records directly
3491                         modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-version-id",modelVersionId);
3492                 }
3493                 else if( modelName != null && !modelName.equals("") ){
3494                         modVerVertsIter = this.engine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE,"model-ver").has("model-name",modelName);
3495                 }
3496                 else if( modelInvId != null && !modelInvId.equals("") ){
3497                         modVerVertsIter = getModVersUsingModelInvId(transId, fromAppId, modelInvId);
3498                 }
3499                 else {
3500                         String msg = "Neither modelVersionId, modelInvariantId, nor modelName passed to: getModelVerTopWidgetType() ";
3501                         throw new AAIException("AAI_6120", msg);
3502                 }
3503                 
3504                 if( !modVerVertsIter.hasNext() ){
3505                         String emsg = "model-ver record(s) could not be found for model data passed:  modelInvariantId = [" + modelInvId +
3506                                         "], modeVersionId = [" + modelVersionId + "], modelName = [" + modelName + "]\n";
3507                         throw new AAIException("AAI_6114", emsg);
3508                 }
3509                 else { 
3510                         String lastNT = "";
3511                         if( !modVerVertsIter.hasNext() ){
3512                                 String emsg = "model-ver record(s) could not be found for model data passed:  modelInvariantId = [" + modelInvId +
3513                                                 "], modeVersionId = [" + modelVersionId + "], modelName = [" + modelName + "]\n";
3514                                 throw new AAIException("AAI_6114", emsg);
3515                         }
3516                         while( modVerVertsIter.hasNext() ){
3517                                 Vertex tmpModVerVtx = (Vertex) modVerVertsIter.next();
3518                                 String tmpNT = getModelVerTopWidgetType( tmpModVerVtx, "" );
3519                                 if( lastNT != null && !lastNT.equals("") ){
3520                                         if( !lastNT.equals(tmpNT) ){
3521                                                 String emsg = "Different top-node-types (" + tmpNT + ", " + lastNT 
3522                                                                 + ") found for model data passed.  (" +
3523                                                                 " modelVersionId = [" + modelVersionId + 
3524                                                                 "], modelId = [" + modelInvId +
3525                                                                 "], modelName = [" + modelName +
3526                                                                 "])\n";
3527                                                 throw new AAIException("AAI_6114", emsg);
3528                                         }
3529                                 }
3530                                 lastNT = tmpNT;
3531                                 nodeType = tmpNT;
3532                         }
3533                 }
3534                 
3535                 return nodeType;
3536                 
3537         }// End getModelVerTopWidgetType()
3538         
3539                         
3540         /**
3541          * Gets the widget type that this model-ver starts with.
3542          *
3543          * @param modVerVtx the model-version vtx
3544          * @param elementTrail the element trail
3545          * @return the widget type of the starting node of this model
3546          * @throws AAIException the AAI exception
3547          */
3548         public String getModelVerTopWidgetType(Vertex modVerVtx, String elementTrail )
3549                                 throws AAIException {
3550                 // Get the associated nodeType (Ie. aai-node-type / widget-type) for a model-ver.
3551                 // NOTE -- if the element points to a resource or service model, then we'll return the
3552                 //        widget-type of the first element (crown widget) for that model.
3553                 String modelType = getModelTypeFromModelVer( modVerVtx, elementTrail );
3554                 if( modelType == null ){
3555                         String msg = " Could not determine modelType in getModelVerTopWidgetType().  elementTrail = [" + elementTrail + "].";
3556                         throw new AAIException("AAI_6132", msg);
3557                 }
3558                   
3559                 String thisElementNodeType = "?";
3560                 if( modelType.equals("widget") ){
3561                         // NOTE: for models that have model-type = "widget", their child model-ver nodes will
3562                         //       have "model-name" which maps directly to aai-node-type (all model-ver's under one
3563                         //       model should start with the same widget-type, so we only need to look at one).
3564                         thisElementNodeType = modVerVtx.<String>property("model-name").orElse(null);
3565                         if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){
3566                                 String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "].";
3567                                 throw new AAIException("AAI_6132", msg);
3568                         }
3569                 }
3570                 else if( modelType.equals("resource") || modelType.equals("service") ){
3571                         Vertex relatedTopElementVtx = getTopElementForSvcOrResModelVer( modVerVtx, elementTrail );
3572                         Vertex relatedModVerVtx = getModelVerThatElementRepresents( relatedTopElementVtx, elementTrail );
3573                         thisElementNodeType = relatedModVerVtx.<String>property("model-name").orElse(null);
3574                         if( (thisElementNodeType == null) || thisElementNodeType.equals("") ){
3575                                 String msg = "Could not find model-name for the widget model pointed to by element at [" + elementTrail + "].";
3576                                 throw new AAIException("AAI_6132", msg);
3577                         }
3578                 }
3579                 else {
3580                         String msg = " Unrecognized model-type = [" + modelType + "] pointed to by element at [" + elementTrail + "].";
3581                         throw new AAIException("AAI_6132", msg);
3582                 }
3583                  
3584                 return thisElementNodeType;
3585                 
3586         }// getModelVerTopWidgetType()
3587         
3588         
3589         /**
3590          * Validate model.
3591          *
3592          * @param transId the trans id
3593          * @param fromAppId the from app id
3594          * @param modelNameVersionId the model name version id
3595          * @param apiVersion the api version
3596          * @throws AAIException the AAI exception
3597          */
3598         public void validateModel(String transId, String fromAppId, String modelVersionIdVal, String apiVersion ) 
3599                                 throws AAIException {
3600         
3601                 // Note - this will throw an exception if the model either can't be found, or if 
3602                 //     we can't figure out its topology map.
3603                 Vertex modelVerVtx = getNodeUsingUniqueId(transId, fromAppId, "model-ver",
3604                                 "model-version-id", modelVersionIdVal);
3605                 if( modelVerVtx == null ){
3606                         String msg = " Could not find model-ver with modelVersionId = [" + modelVersionIdVal + "].";
3607                         throw new AAIException("AAI_6114", msg);
3608                 }
3609                 else {
3610                         Multimap<String, String> topoMap = genTopoMap4ModelVer( transId, fromAppId,
3611                                         modelVerVtx, modelVersionIdVal);
3612                         String msg = " modelVer [" + modelVersionIdVal + "] topo multiMap looks like: \n[" + topoMap + "]";
3613                         System.out.println("INFO --  " + msg );
3614                 }
3615                 return;
3616                 
3617         }// End validateModel()
3618         
3619         
3620         /**
3621          * Validate named query.
3622          *
3623          * @param transId the trans id
3624          * @param fromAppId the from app id
3625          * @param namedQueryUuid the named query uuid
3626          * @param apiVersion the api version
3627          * @throws AAIException the AAI exception
3628          */
3629         public void validateNamedQuery(String transId, String fromAppId, String namedQueryUuid, String apiVersion ) 
3630                                 throws AAIException {
3631         
3632                 // Note - this will throw an exception if the named query either can't be found, or if 
3633                 //     we can't figure out its topology map.
3634                 Vertex nqVtx = getNodeUsingUniqueId(transId, fromAppId, "named-query",
3635                                 "named-query-uuid", namedQueryUuid);
3636                 
3637                 if( nqVtx == null ){
3638                         String msg = " Could not find named-query with namedQueryUuid = [" + namedQueryUuid + "].";
3639                         throw new AAIException("AAI_6114", msg);
3640                 }
3641                 else {
3642                         //Multimap<String, String> topoMap = genTopoMap4NamedQ( "junkTransId", "junkFromAppId", 
3643                                 //      graph, nqVtx, namedQueryUuid );
3644                         //System.out.println("DEBUG -- for test only : --- ");
3645                         //System.out.println("DEBUG -- topomap = [" + topoMap + "]");
3646                 }
3647                 return;
3648                 
3649         }// End validateNamedQuery()
3650         
3651           
3652         /**
3653          * Show result set.
3654          *
3655          * @param resSet the res set
3656          * @param levelCount the level count
3657          */
3658         public void showResultSet(ResultSet resSet, int levelCount ) {
3659                 
3660                   levelCount++;
3661                   String propsStr = "";
3662                   for( int i= 1; i <= levelCount; i++ ){
3663                           propsStr = propsStr + "-";
3664                   }
3665                   if( resSet.getVert() == null ){
3666                           return;
3667                   }
3668                   String nt = resSet.getVert().<String>property(AAIProperties.NODE_TYPE).orElse(null);
3669                   propsStr = propsStr +  "[" + nt + "] ";
3670                  
3671                   //propsStr = propsStr + " newDataDelFlag = " + resSet.getNewDataDelFlag() + ", trail = " + resSet.getLocationInModelSubGraph();
3672                   //propsStr = propsStr + "limitDesc = [" + resSet.getPropertyLimitDesc() + "]";
3673                   propsStr = propsStr + " trail = " + resSet.getLocationInModelSubGraph();
3674                   
3675                   Map<String,Object> overrideHash = resSet.getPropertyOverRideHash();
3676                   if( overrideHash != null  &&  !overrideHash.isEmpty() ){
3677                           for( Map.Entry<String, Object> entry : overrideHash.entrySet() ){
3678                                   String propName = entry.getKey();
3679                                   Object propVal = entry.getValue();
3680                                   propsStr = propsStr + " [" + propName + " = " + propVal + "]";
3681                           }
3682                   }
3683                   else {
3684                           Iterator<VertexProperty<Object>> pI = resSet.getVert().properties();
3685                           while( pI.hasNext() ){
3686                                         VertexProperty<Object> tp = pI.next();
3687                                         if( ! tp.key().startsWith("aai") 
3688                                                         && ! tp.key().equals("source-of-truth")
3689                                                         //&& ! tp.key().equals("resource-version")
3690                                                         && ! tp.key().startsWith("last-mod")
3691                                                         )
3692                                         {
3693                                                 propsStr = propsStr + " [" + tp.key() + " = " + tp.value() + "]";
3694                                         }
3695                           }
3696                   }
3697                   // Show the "extra" lookup values too
3698                   Map<String,Object> extraPropHash = resSet.getExtraPropertyHash();
3699                   if( extraPropHash != null && !extraPropHash.isEmpty() ){
3700                           for( Map.Entry<String, Object> entry : extraPropHash.entrySet() ){
3701                                   String propName = entry.getKey();
3702                                   Object propVal = entry.getValue();
3703                                   propsStr = propsStr + " [" + propName + " = " + propVal.toString() + "]";
3704                           }
3705                   }
3706                   
3707                   System.out.println( propsStr );
3708                   LOGGER.info(propsStr);
3709                   
3710                   if( !resSet.getSubResultSet().isEmpty() ){
3711                           ListIterator<ResultSet> listItr = resSet.getSubResultSet().listIterator();
3712                           while( listItr.hasNext() ){
3713                                   showResultSet( listItr.next(), levelCount );
3714                           }
3715                   }
3716                   
3717           }// end of showResultSet()
3718         
3719         private Iterator<Vertex> traverseIncidentEdges(EdgeType treeType, Vertex startV, String connectedNodeType) throws AAIUnknownObjectException, AAIException {
3720                 QueryBuilder builder = this.engine.getQueryBuilder(startV).createEdgeTraversal(treeType, startV, loader.introspectorFromName(connectedNodeType));
3721                 return builder;
3722         }
3723         
3724         private Iterator<Vertex> traverseIncidentEdges(EdgeType treeType, Vertex startV, String... connectedNodeType) throws AAIUnknownObjectException, AAIException {
3725                 QueryBuilder[] builders = new QueryBuilder[connectedNodeType.length];
3726                 for (int i = 0; i < connectedNodeType.length; i++) {
3727                         builders[i] = this.engine.getQueryBuilder(startV).createEdgeTraversal(EdgeType.TREE, startV, loader.introspectorFromName(connectedNodeType[i]));
3728                 }
3729                 QueryBuilder builder = this.engine.getQueryBuilder(startV).union(builders);
3730                 return builder;
3731         }
3732
3733         private String addDBAliasedSuffix(String propName) {
3734                 return propName + AAIProperties.DB_ALIAS_SUFFIX;
3735         }
3736         
3737         protected String getPropNameWithAliasIfNeeded(String nodeType, String propName) throws AAIUnknownObjectException {
3738                 
3739                 String retPropName = propName;
3740                 if( loader.introspectorFromName(nodeType).getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS).isPresent() ){
3741                         return propName + AAIProperties.DB_ALIAS_SUFFIX;
3742                 }
3743                 return retPropName;
3744         }
3745                   
3746 }
3747