db33e4dcaad53d7524903def1e534cc26e8e5d95
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / dbgen / DbMeth.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
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
21 package org.openecomp.aai.dbgen;
22
23 import java.net.Inet4Address;
24 import java.net.Inet6Address;
25 import java.net.InetAddress;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
35 import org.apache.tinkerpop.gremlin.structure.Direction;
36 import org.apache.tinkerpop.gremlin.structure.Edge;
37 import org.apache.tinkerpop.gremlin.structure.Vertex;
38 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
39
40 import org.openecomp.aai.dbmodel.DbEdgeRules;
41 import org.openecomp.aai.exceptions.AAIException;
42 import org.openecomp.aai.ingestModel.DbMaps;
43 import org.openecomp.aai.ingestModel.IngestModelMoxyOxm;
44 import org.openecomp.aai.serialization.db.EdgeRule;
45 import org.openecomp.aai.serialization.db.EdgeRules;
46 import org.openecomp.aai.util.AAIConfig;
47 import org.openecomp.aai.util.AAIConstants;
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
50 import com.google.common.net.InetAddresses;
51 import com.thinkaurelius.titan.core.TitanEdge;
52 import com.thinkaurelius.titan.core.TitanGraph;
53 import com.thinkaurelius.titan.core.TitanTransaction;
54 import com.thinkaurelius.titan.core.TitanVertex;
55
56
57 /**
58  * General Database-level Utility class.   These methods deal with the database one dataNode / Edge at a time.
59  * Transactions are managed at a higher level by the calling classes by passing in a TitanTransaction object.
60  */
61 public class DbMeth{
62
63         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DbMeth.class);
64         
65         /**
66          * Patch aai node.
67          *
68          * @param transId the trans id
69          * @param fromAppId the from app id
70          * @param graph the graph
71          * @param nodeType the node type
72          * @param propHash the prop hash
73          * @param depNodeVal the dep node val
74          * @param apiVersion the api version
75          * @return TitanVertex
76          * @throws AAIException the AAI exception
77          */
78         public static TitanVertex patchAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
79                         HashMap <String,Object> propHash, TitanVertex depNodeVal, String apiVersion ) throws AAIException{
80                 // If they're calling patchAaiNode, then we only want to add/update the properties that they
81                 // pass us in the propHash.  If there are others already in the DB, we leave them alone.
82
83                 // Note: to be really official, we'd throw an error if the node wasn't already in the db.
84                 boolean[] objectExists = new boolean[1];
85                 objectExists[0] = true;
86                 Boolean patchOnly = true;
87                 TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists);
88                 return( tv );
89
90         } // end of patchAaiNode()
91         
92         /**
93          * Patch aai node.
94          *
95          * @param transId the trans id
96          * @param fromAppId the from app id
97          * @param graph the graph
98          * @param nodeType the node type
99          * @param propHash the prop hash
100          * @param depNodeVal the dep node val
101          * @return the titan vertex
102          * @throws AAIException the AAI exception
103          */
104         @Deprecated
105         public static TitanVertex patchAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
106                         HashMap <String,Object> propHash, TitanVertex depNodeVal) throws AAIException{
107                 return patchAaiNode( transId,  fromAppId,  graph,  nodeType, 
108                                 propHash,  depNodeVal, null );
109         }
110         
111         /**
112          * Persist aai node.
113          *
114          * @param transId the trans id
115          * @param fromAppId the from app id
116          * @param graph the graph
117          * @param nodeType the node type
118          * @param propHash the prop hash
119          * @param depNodeVal the dep node val
120          * @param patchOnly the patch only
121          * @param apiVersion the api version
122          * @return the titan vertex
123          * @throws AAIException the AAI exception
124          */
125         public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
126                         HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, String apiVersion) throws AAIException{
127                 boolean[] objectExists = new boolean[1];
128                 objectExists[0] = false;
129                 return persistAaiNodeBASE( transId,  fromAppId,  graph,  nodeType, 
130                                  propHash,  depNodeVal, patchOnly, apiVersion, objectExists);
131         }
132         
133         /**
134          * Persist aai node.
135          *
136          * @param transId the trans id
137          * @param fromAppId the from app id
138          * @param graph the graph
139          * @param nodeType the node type
140          * @param propHash the prop hash
141          * @param addIfNotFound the add if not found
142          * @param depNodeVal the dep node val
143          * @param apiVersion the api version
144          * @return the titan vertex
145          * @throws AAIException the AAI exception
146          */
147         @Deprecated
148         public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
149                         HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion) throws AAIException{
150                 // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us.  That is, if
151                 // there is already a record in the DB, but they do not pass some of the existing properties, they should
152                 // be cleared from the DB.    Since we want to take care of all properties, we pass patchOnly = false
153                 Boolean patchOnly = false;
154                 boolean[] objectExists = new boolean[1];
155                 objectExists[0] = false;
156                 TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists);
157                 return( tv );
158         } 
159         
160         /**
161          * Persist aai node.
162          *
163          * @param transId the trans id
164          * @param fromAppId the from app id
165          * @param graph the graph
166          * @param nodeType the node type
167          * @param propHash the prop hash
168          * @param addIfNotFound the add if not found
169          * @param depNodeVal the dep node val
170          * @return the titan vertex
171          * @throws AAIException the AAI exception
172          */
173         @Deprecated
174         public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
175                         HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal) throws AAIException{
176                 // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us.  That is, if
177                 // there is already a record in the DB, but they do not pass some of the existing properties, they should
178                 // be cleared from the DB.    Since we want to take care of all properties, we pass patchOnly = false
179                 Boolean patchOnly = false;
180                 boolean[] objectExists = new boolean[1];
181                 objectExists[0] = false;
182                 TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, null, objectExists);
183                 return( tv );
184         } // end of persistAaiNode()
185         
186         /**
187          * Persist aai node.
188          *
189          * @param transId the trans id
190          * @param fromAppId the from app id
191          * @param graph the graph
192          * @param nodeType the node type
193          * @param propHash the prop hash
194          * @param addIfNotFound the add if not found
195          * @param depNodeVal the dep node val
196          * @param apiVersion the api version
197          * @param objectExists the object exists
198          * @return TitanVertex
199          * @throws AAIException the AAI exception
200          */
201         public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
202                         HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion, boolean[] objectExists) throws AAIException{
203                 Boolean patchOnly = false;
204                 // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us.  That is, if
205                 // there is already a record in the DB, but they do not pass some of the existing properties, they should
206                 // be cleared from the DB.    Since we want to take care of all properties, we pass patchOnly = false
207                 TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, null);
208                 return( tv );
209         } 
210         
211         /**
212          * Persist aai node.
213          *
214          * @param transId the trans id
215          * @param fromAppId the from app id
216          * @param graph the graph
217          * @param nodeType the node type
218          * @param propHash the prop hash
219          * @param addIfNotFound the add if not found
220          * @param depNodeVal the dep node val
221          * @param apiVersion the api version
222          * @param objectExists the object exists
223          * @param thisNodeVertex the this node vertex
224          * @return the titan vertex
225          * @throws AAIException the AAI exception
226          */
227         public static TitanVertex persistAaiNode(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
228                         HashMap <String,Object> propHash, Boolean addIfNotFound, TitanVertex depNodeVal, String apiVersion, boolean[] objectExists, TitanVertex thisNodeVertex) throws AAIException{
229                 Boolean patchOnly = false;
230                 // If they're calling persistAaiNode, then we want to make the Db look like whatever they pass us.  That is, if
231                 // there is already a record in the DB, but they do not pass some of the existing properties, they should
232                 // be cleared from the DB.    Since we want to take care of all properties, we pass patchOnly = false
233                 TitanVertex tv = persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, thisNodeVertex);
234                 return( tv );
235         } 
236         
237         /**
238          * Persist aai node BASE.
239          *
240          * @param transId the trans id
241          * @param fromAppId the from app id
242          * @param graph the graph
243          * @param nodeType the node type
244          * @param propHash the prop hash
245          * @param depNodeVal the dep node val
246          * @param patchOnly the patch only
247          * @param apiVersion the api version
248          * @param objectExists the object exists
249          * @return the titan vertex
250          * @throws AAIException the AAI exception
251          */
252         public static TitanVertex persistAaiNodeBASE(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
253                         HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, 
254                         String apiVersion, boolean[] objectExists) throws AAIException{
255                 return persistAaiNodeBASE(transId, fromAppId, graph, nodeType, propHash, depNodeVal, patchOnly, apiVersion, objectExists, null);
256         }
257         
258         /**
259          * Persist aai node BASE.
260          *
261          * @param transId the trans id
262          * @param fromAppId the from app id
263          * @param graph the graph
264          * @param nodeType the node type
265          * @param propHash the prop hash
266          * @param depNodeVal the dep node val
267          * @param patchOnly the patch only
268          * @param apiVersion the api version
269          * @param objectExists the object exists
270          * @param thisNodeVertex the this node vertex
271          * @return the titan vertex
272          * @throws AAIException the AAI exception
273          */
274         public static TitanVertex persistAaiNodeBASE(String transId, String fromAppId, TitanTransaction graph, String nodeType, 
275                         HashMap <String,Object> propHash, TitanVertex depNodeVal, Boolean patchOnly, 
276                         String apiVersion, boolean[] objectExists, TitanVertex thisNodeVertex) throws AAIException{
277                 
278                 if( graph == null ){
279                         throw new AAIException("AAI_6101", "null graph object passed to persistAaiNodeBASE()"); 
280                 }
281                 
282                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
283                 
284                 boolean useDepNode = false;
285                 String resourceVersion = null;
286                 if( propHash.containsKey("resource-version") ){
287                         resourceVersion = (String)(propHash.get("resource-version")); 
288                 }       
289                 String aaiUniqueKeyVal = null;
290                 if( propHash.containsKey("aai-unique-key") ){
291                         // Note -- we are assuming that nobody is monkeying with this.   The 16-07 first-pass theory
292                         //    is that the REST layer is always gonna generate this or pass it through.
293                         aaiUniqueKeyVal = (String)(propHash.get("aai-unique-key")); 
294                         propHash.remove("aai-unique-key");
295                 }       
296                 
297                 if( needsADepNode4Uniqueness(transId, fromAppId, nodeType, apiVersion) ){
298                         // This kind of node needs a dependent node (for uniqueness)
299                         if( depNodeVal == null ){
300                                 // They should have passed in the node that this one depends on
301                                 throw new AAIException("AAI_6109", "null dependentNode object passed to persistAaiNodeBASE() but " + nodeType + " requires one."); 
302                         }
303                         else if( ! nodeTypeACanDependOnB(transId, fromAppId, nodeType, depNodeVal.<String>property("aai-node-type").orElse(null), apiVersion) ){
304                                 // They should have passed in the right type of node as the dependent node
305                                 throw new AAIException("AAI_6109", "dependentNode of type " + depNodeVal.<String>property("aai-node-type").orElse(null) + " passed to persistAaiNodeBASE() for nodeType" + nodeType + "."); 
306                         }
307                         useDepNode = true;
308                 }
309                 else {
310                         depNodeVal = null;
311                 }
312                 
313                 // Note: as of 1607, we no longer validate property names since that's covered by the REST layer.
314                 // Same goes for required fields (as of 1602)
315
316                 // Special ip-address validation for ipAddr nodes only...   This will go away when we go to YANG and
317                 // do validations like this up at that layer.
318                 if( nodeType.equals("ipaddress") ){
319                         // Note - this will throw an exception if the ipAddress is using a bad format
320                         ipAddressFormatOK( transId, fromAppId, (String)propHash.get("addr"), (String)propHash.get("version") );
321                 }
322
323                 // Use the key-fields/dependentNode to check if this is an add or an update
324                 // We assume that all NodeTypes at least one key-property defined.  A dependentNode is optional.
325                 if( ! dbMaps.NodeKeyProps.containsKey(nodeType) ){
326                         // Problem if no key Properties defined for this nodeType  
327                         String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP);
328                         throw new AAIException("AAI_6105", "No node-key-properties defined in dbMaps for nodeType = " + nodeType + " (ver=" + defVer + ")"); 
329                 }
330
331                 Boolean hasAltKey1 = false;
332                 HashMap <String,Object>nodeAltKey1PropsHash = new HashMap<String,Object>();
333                 Collection <String> altKey1Props = getNodeAltKey1PropNames(transId, fromAppId, nodeType, apiVersion);
334                 if( altKey1Props != null ){
335                         Iterator <String> altKey1PropI = altKey1Props.iterator();
336                         while( altKey1PropI.hasNext() ){
337                                 String propName = altKey1PropI.next();
338                                 // NOTE: alt-keys are not always required fields.  If it is null or blank, we won't 
339                                 //      do alt-key checks on it.
340                                 Object value = propHash.get(propName); 
341                                 if( value != null && !value.toString().equals("") ){
342                                         hasAltKey1 = true;
343                                         nodeAltKey1PropsHash.put(propName, value);
344                                 }
345                         }
346                 }
347                 HashMap <String,Object>nodeKeyPropsHash = new HashMap<String,Object>();
348                 Collection <String> keyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion);
349                 Iterator <String> keyPropI = keyProps.iterator();
350                 while( keyPropI.hasNext() ){
351                         String propName = keyPropI.next();
352                         
353                         Object value = propHash.get(propName); 
354                         nodeKeyPropsHash.put(propName, value);
355                 }
356
357                 // Check if this node is already in the database based on the Primary Key Info
358                 TitanVertex existingVert = thisNodeVertex;
359                 boolean foundTheNodeInDb = true;
360                         
361                 if (existingVert == null) { 
362                         try {
363                                 existingVert = getUniqueNode( transId, fromAppId, graph, nodeType, nodeKeyPropsHash, depNodeVal, apiVersion );
364                         }
365                         catch (AAIException e) {
366                                 if (e.getErrorObject().getErrorCode().equals("6114")) {
367                                         foundTheNodeInDb = false;
368                                 }
369                                 else {
370                                         throw e;
371                                 }
372                         }
373                 }
374                                 
375                 // this is so the notification knows whether or not the operation was an UPDATE or a CREATe
376                 objectExists[0] = foundTheNodeInDb;
377                 if( foundTheNodeInDb ){
378                         // A record was found in the DB using the PK.  
379                         if( needToDoResourceVerCheck(apiVersion, patchOnly) ){
380                                 // Need to check that they knew what they were updating
381                                 String existingResVer = existingVert.<String>property("resource-version").orElse(null);
382                                 if( resourceVersion == null || resourceVersion.equals("") ){
383                                         throw new AAIException("AAI_6130", "Resource-version not passed for update of = " + nodeType + ", " + nodeKeyPropsHash.toString()); 
384                                 }
385                                 else if( (existingResVer != null) && !resourceVersion.equals(existingResVer) ){
386                                         throw new AAIException("AAI_6131", "Resource-version " + resourceVersion + " MISMATCH WITH EXISTING " + existingResVer + " for update of = " + nodeType + ", " + nodeKeyPropsHash.toString()); 
387                                 }
388                         }
389                         
390                         // Need to ensure that the Alternate key isn't changing to a value that points to a different existing node.   
391                         // It is ok if it points to nothing -- that would just be an update for this node.   It's also ok if 
392                         // it points to this (existing) node - that just means that it wasn't being updated.
393                         if( hasAltKey1 ){
394                                 try {
395                                         TitanVertex chkVert = getUniqueNode( transId, fromAppId, graph, nodeType, nodeAltKey1PropsHash, depNodeVal, apiVersion );
396                                         if( ! chkVert.id().toString().equals(existingVert.id().toString()) ){
397                                                 throw new AAIException("AAI_6117", "In-Use AlternateKey value passed for update of nodeType = " + nodeType); 
398                                         }
399                                 }
400                                 catch (AAIException e) {
401                                         if(! e.getErrorObject().getErrorCode().equals("6114") ){
402                                                 throw e;
403                                         }
404                                 }
405                         }
406                 }
407                 else {
408                         // Note not in the DB -- This will be an ADD of a new node
409                         //              a) make sure they didn't say they were just doing "patchOnly" which cannot be an ADD.
410                         //              b) if there is an alternate key, we need to make sure the AK isn't already in use by somebody else.
411                         if( patchOnly ){
412                                 String depMsg = "";
413                                 if( useDepNode ){
414                                         depMsg = " plus dependent node. ";
415                                 }
416                                 throw new AAIException("AAI_6114", "Patch Request, but no Node of type " + nodeType + " found for properties: [" + propHash + "] " + depMsg);
417                         }
418                         
419                         if( needToDoResourceVerCheck(apiVersion, patchOnly) && (resourceVersion != null) && !resourceVersion.equals("") ){
420                                 throw new AAIException("AAI_6131", "Resource-version was passed in, but this is an ADD of a " + nodeType + ", with these params: " + nodeKeyPropsHash.toString()); 
421                         }
422                         if( hasAltKey1 ){
423                                 try {
424                                         getUniqueNode( transId, fromAppId, graph, nodeType, nodeAltKey1PropsHash, depNodeVal, apiVersion );
425                                         // Since the Primary Key for this nodeType wasn't found in the DB yet, the fact that
426                                         // we are able to find a record (no "6114" exception thrown) using the Alternate-Key is an error.  
427                                         // We can't create a new node that uses an AK that's already in use.
428                                         throw new AAIException("AAI_6117", "Conflicting Key and Alternate-Key values passed for add of nodeType = " + nodeType); 
429                                 }
430                                 catch (AAIException e) {
431                                         if(! e.getErrorObject().getErrorCode().equals("6114") ){
432                                                 throw e;
433                                         }
434                                 }
435                         }
436                 }
437
438                 // ------------- Done with checking.  Do the add or update to the dB -----------------------
439
440                 if( foundTheNodeInDb ){
441                         long unixTimeNow = System.currentTimeMillis() / 1000L;
442                         // ----- This is an UPDATE ------
443                         
444                         
445                         String existingSourceOfTruth = fromAppId;  // default value if we can't get the old one
446                         Object tmpOb = existingVert.<Object>property("source-of-truth").orElse(null);
447                         if( tmpOb != null ){
448                                 existingSourceOfTruth = tmpOb.toString();
449                         }
450                         long existingCreateTs = unixTimeNow;  // default value if we can't get the old one
451                         tmpOb = existingVert.<Object>property("aai-created-ts").orElse(null);
452                         if( tmpOb != null ){
453                                 existingCreateTs = (long) tmpOb;
454                         }
455                         
456                         String msg = "UPDATE vertex of type = [" + nodeType + "] "; 
457                         if( useDepNode ){
458                                 String depNType = depNodeVal.<String>property("aai-node-type").orElse(null);
459                                 HashMap <String, Object> depNodePropKeysHash = getNodeKeyPropHash(transId, fromAppId, graph, depNodeVal);
460                                 LOGGER.info("UPDATE existing node: type = " + nodeType + ", key(s) = [" + nodeKeyPropsHash + 
461                                                 "] which rides on dependent node: type = " + depNType + ", with key(s) = [" + depNodePropKeysHash + "].");
462                         }
463                         else {
464                                 LOGGER.info("UPDATE existing node: type = " + nodeType + ", key(s) = [" + nodeKeyPropsHash + "] (no dep. node).");
465                         }
466                         String removeList = "";
467                         if( ! patchOnly ){
468                                 // They are updating an existing record, and they want us to "process all defined properties" (not just patch)   
469                                 // So we will see if the node has any properties that were not passed-in.  Those need to be removed.
470                                 Collection <String> propCol =  dbMaps.NodeProps.get(nodeType);
471                                 Iterator <String> propIter = propCol.iterator();
472                                 while( propIter.hasNext() ){
473                                         String propName = propIter.next();
474                                         if( ! propHash.containsKey(propName) && !DbEdgeRules.ReservedPropNames.containsKey(propName)){  
475                                                 if( thisPropertyWasPutByNewerVersionOfCode(apiVersion, nodeType, propName) ){
476                                                         //   we must be using an older version of code here - but the property that
477                                                         //   has not been passed in this persist call is one that this older version of
478                                                         //   the database did not know about.  So leave it alone.
479                                                 }
480                                                 else {
481                                                         removeList = removeList + "," + propName;
482                                                         existingVert.property(propName).remove();
483                                                 }
484                                         }
485                                 }
486                         }
487                         if( !removeList.equals("") ){
488                                 LOGGER.info("Removed these props on update: [" + removeList + "]");
489                         }
490                         for( Map.Entry<String, Object> entry : propHash.entrySet() ){
491                                 // update the parameters that have been passed in (except the key-properties)
492                                 //                  taking away the key-property check.  We will now allow this since
493                                 //                  the keys were used to identify this node, so they should be good and
494                                 //                  there are times when Titan resolves conflicts by only using the 
495                                 //                  data set in an update - and was losing our key info... 
496                                 //                  Similar to the change noted below.
497                                 //if( ! nodeKeyPropsHash.containsKey(entry.getKey()) ){
498                                 //      existingVert.setProperty( entry.getKey(), entry.getValue() );
499                                 //}
500                                 if( ! entry.getKey().equals("resource-version") ){
501                                         boolean nonSingleCardinality = false;
502                                         boolean setSoNoDupes = false;
503                                         if( checkPropCardinality(entry.getKey(), "Set") ){
504                                                 nonSingleCardinality = true;
505                                                 setSoNoDupes = true;
506                                         }
507                                         else if( checkPropCardinality(entry.getKey(), "List") ){
508                                                 nonSingleCardinality = true;
509                                         }
510                                         
511                                         Iterator <Object> valIter = null;
512                                         if( nonSingleCardinality ){
513                                                 String className = entry.getValue().getClass().getSimpleName();
514                                                 if( className.equals("ArrayList") ){
515                                                         valIter = ((ArrayList)(entry.getValue())).iterator();
516                                                 }
517                                                 else if( className.equals("List") ){
518                                                         valIter = ((List)(entry.getValue())).iterator();
519                                                 }
520                                                 else if( className.equals("Set") ){
521                                                         valIter = ((Set)(entry.getValue())).iterator();
522                                                 }
523                                         }
524                                         
525                                         if( nonSingleCardinality ){
526                                                 // This property has Cardinality of List or Set - which need to be handled carefully
527                                                 // Note -- for Lists or Sets, we assume they are of dataType String - that is all
528                                                 //       the Rest layer supports at the moment (16-02)
529                                                 ArrayList <String> currentData = new ArrayList <String> ();
530                                                 if( patchOnly ){
531                                                         // When patching - gotta know what's already in the db
532                                                         Iterator<VertexProperty<Object>> existingPropsIter =  (existingVert.properties(entry.getKey()));
533                                                         if( existingPropsIter != null ){
534                                                                 while( existingPropsIter.hasNext() ){
535                                                                         String existingVal = existingPropsIter.next().value().toString();
536                                                                         currentData.add( existingVal );
537                                                                 }
538                                                         }
539                                                 }
540                                                 else {
541                                                         // Since this is not a patch-update, we first have to clear out what is currently in the db.
542                                                         existingVert.property(entry.getKey()).remove();
543                                                 }
544                                                 
545                                                 if( valIter != null ){
546                                                         while( valIter.hasNext() ){
547                                                                 Object thisVal = valIter.next();        
548                                                                 if( setSoNoDupes ){
549                                                                         // For Sets, we need to check that the data isn't already in the db or wasn't passed
550                                                                         // in to us twice in the propHash.  Otherwise Titan throws an exception (instead of just ignoring it...)
551                                                                         if( !currentData.contains(thisVal) ){
552                                                                                 // We don't have this data yet, so add it to the Set
553                                                                                 existingVert.property( entry.getKey(), thisVal );
554                                                                                 currentData.add( thisVal.toString() );
555                                                                         }
556                                                                 }
557                                                                 else {
558                                                                         // For List data types, it's ok to have duplicate values in the db (why would we want this?)
559                                                                         existingVert.property( entry.getKey(), thisVal );
560                                                                 }
561                                                         }
562                                                 }
563                                         }
564                                         else {
565                                                 // This is a normal, "Cardinality = SINGLE" kind of property
566                                                 // ResourceVersion is not populated based on passed-in data, it is set along with other internal properties below.
567                                                 //Object cleanVal = convertTypeIfNeeded( entry.getKey(), entry.getValue() );
568                                                 //existingVert.setProperty( entry.getKey(),  cleanVal );
569                                                 // ********************************
570                                                 existingVert.property( entry.getKey(), entry.getValue() );
571                                         }
572                                 }
573                         }
574
575                         // DEBUG - trying to deal with the case where simultaneous PUTs
576                         //    cause our db to wind up with a vertex that does not have these three properties filled in.
577                         existingVert.property( "aai-node-type", nodeType );
578                         existingVert.property( "aai-created-ts", existingCreateTs );
579                         existingVert.property( "source-of-truth", existingSourceOfTruth );
580                         
581                         if( aaiUniqueKeyVal != null ){
582                                 existingVert.property( "aai-unique-key", aaiUniqueKeyVal );
583                         }
584                         
585                         existingVert.property( "aai-last-mod-ts", unixTimeNow );
586                         String resVers = "" + unixTimeNow; 
587                         existingVert.property( "resource-version", resVers );
588                         existingVert.property( "last-mod-source-of-truth", fromAppId );
589                         
590                         LOGGER.info(msg + ", [aai-last-mod-ts]/[" + unixTimeNow + "]");
591                         
592                         return( existingVert );
593                 }
594                 else{ 
595                         // ----- Not found in the DB, This must be an ADD ------
596                         if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
597                                 throw new AAIException("AAI_6120", "nodeTypeCategory " + nodeType + " cannot be used to ADD a node.  Need to pass a valid nodeType"); 
598                         }
599
600                         TitanVertex tiVnew = graph.addVertex( nodeType );
601
602                         String msg = "ADD vertex of type = [" + nodeType + "] ";       
603                         if( depNodeVal != null ){
604                                 String depNType = depNodeVal.<String>property("aai-node-type").orElse(null);
605                                 HashMap <String, Object> depNodePropKeysHash = getNodeKeyPropHash(transId, fromAppId, graph, depNodeVal);
606                                 msg = msg + " onto dependent node: type = " + depNType + ", which has key(s) = [" + depNodePropKeysHash + 
607                                                 "].  New Node Prop/values = ";
608                         }
609                         else {
610                                 msg = msg + " Note: no dependent node.  New Node Prop/values = ";
611                         }
612                         boolean first = true;
613                         for( Map.Entry<String, Object> entry : propHash.entrySet() ){
614                                 if( ! entry.getKey().equals("resource-version") ){
615                                         if( first ){
616                                                 msg = msg + " [" + entry.getKey() + "]/[" + entry.getValue() + "]";
617                                                 first = false;
618                                         }
619                                         else {
620                                                 msg = msg + ", [" + entry.getKey() + "]/[" + entry.getValue() + "]";
621                                         }
622                                         
623                                         boolean nonSingleCardinality = false;
624                                         boolean setSoNoDupes = false;
625                                         if( checkPropCardinality(entry.getKey(), "Set") ){
626                                                 nonSingleCardinality = true;
627                                                 setSoNoDupes = true;
628                                         }
629                                         else if( checkPropCardinality(entry.getKey(), "List") ){
630                                                 nonSingleCardinality = true;
631                                         }
632                                         
633                                         Iterator <Object> valIter = null;
634                                         if( nonSingleCardinality ){
635                                                 String className = entry.getValue().getClass().getSimpleName();
636                                                 if( className.equals("ArrayList") ){
637                                                         valIter = ((ArrayList)(entry.getValue())).iterator();
638                                                 }
639                                                 else if( className.equals("List") ){
640                                                         valIter = ((List)(entry.getValue())).iterator();
641                                                 }
642                                                 else if( className.equals("Set") ){
643                                                         valIter = ((Set)(entry.getValue())).iterator();
644                                                 }
645                                         }
646                                         
647                                         if( nonSingleCardinality ){
648                                                 // This property has Cardinality of List or Set - which need to be handled carefully
649                                                 ArrayList <String> currentData = new ArrayList <String> ();
650                                                 if( valIter != null ){
651                                                         while( valIter.hasNext() ){
652                                                                 Object thisVal = valIter.next();        
653                                                                 if( setSoNoDupes ){
654                                                                         // For Sets, we need to check that they're not passing us duplicate data in propHash.
655                                                                         // Otherwise Titan throws an exception (instead of just ignoring it...)
656                                                                         if( !currentData.contains(thisVal) ){
657                                                                                 // We don't have this data yet, so add it to the Set
658                                                                                 tiVnew.property( entry.getKey(), thisVal );
659                                                                                 currentData.add( thisVal.toString() );
660                                                                         }
661                                                                 }
662                                                                 else {
663                                                                         // For List data types, it's ok to have duplicate values in the db (why would we want this?)
664                                                                         tiVnew.property( entry.getKey(), thisVal );
665                                                                 }
666                                                         }
667                                                 }
668                                         }
669                                         else {
670                                                 // This is a normal, "Cardinality = SINGLE" kind of property
671                                                 // ResourceVersion is not populated based on passed-in data, it is set along with other internal properties below.
672                                                 tiVnew.property( entry.getKey(), entry.getValue() );
673                                         }
674                                 }
675                         }
676                         
677                         tiVnew.property( "aai-node-type", nodeType );
678                         //long unixTime = System.currentTimeMillis() / 1000L;
679                         long unixTime = System.currentTimeMillis();
680                         tiVnew.property( "aai-created-ts", unixTime );
681                         tiVnew.property( "aai-last-mod-ts", unixTime );
682                         String resVers = "" + unixTime; 
683                         tiVnew.property( "resource-version", resVers );
684                         tiVnew.property( "source-of-truth", fromAppId );
685                         tiVnew.property( "last-mod-source-of-truth", fromAppId );
686                         if( aaiUniqueKeyVal != null ){
687                                 tiVnew.property( "aai-unique-key", aaiUniqueKeyVal );
688                         }
689                         
690                         LOGGER.info(msg + ", [aai-created-ts]/[" + unixTime + "]");
691                         return( tiVnew );
692                 }
693
694         } // end of persistAaiNodeBASE()
695
696         
697         /**
698          * Need to do resource ver check.
699          *
700          * @param apiVersion the api version
701          * @param patchOnlyFlag the patch only flag
702          * @return the boolean
703          * @throws AAIException the AAI exception
704          */
705         public static Boolean needToDoResourceVerCheck(String apiVersion, Boolean patchOnlyFlag)
706                         throws AAIException{
707                 
708                 if( patchOnlyFlag ){
709                         // we do not do resource checking for patch requests.
710                         return false;
711                 }
712                 
713                 String resourceCheckOnFlag = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG);
714                 
715                 int apiVerInt = cleanUpApiVersion(apiVersion);
716                 
717                 if( (resourceCheckOnFlag != null) && resourceCheckOnFlag.equals("true") ){
718                         // Only do the check if the resource enable flag is set to "true"
719                         if( apiVerInt > 4 ){
720                                 // We're only doing the resource version checks for v5 and later
721                                 return true;
722                         }
723                 }
724                 
725                 return false;
726         }// End needToDoResourceVerCheck()
727
728         
729         /**
730          * Clean up api version.
731          *
732          * @param apiVersionString the api version string
733          * @return the int
734          * @throws AAIException the AAI exception
735          */
736         private static int cleanUpApiVersion( String apiVersionString ) throws AAIException {
737                 // Note: we expect an apiVersion to start with the letter "v", followed by an integer. 
738                 
739                 int versionInt = 0;
740                 String verStr = apiVersionString;
741                 if( (apiVersionString == null) || (apiVersionString.length() < 2) ){
742                         // Passed in version doesn't look right
743                         verStr = org.openecomp.aai.util.AAIApiVersion.get();
744                 }
745                 versionInt = getVerNumFromVerString( verStr );
746                 
747                 return versionInt;
748         }
749         
750         /**
751          * Gets the ver num from ver string.
752          *
753          * @param versionString the version string
754          * @return the ver num from ver string
755          * @throws AAIException the AAI exception
756          */
757         private static int getVerNumFromVerString( String versionString )throws AAIException {
758                 int versionInt = 0;
759                 if( versionString == null || versionString.length() < 2 ){
760                         throw new AAIException("AAI_6121", " Bad Version (format) passed to getVerNumFromVerString: [" + versionString + "]."); 
761                 }
762                 
763                 int strLen = versionString.length();
764                 // We assume that a version looks like "v" followed by an integer
765                 if( ! versionString.substring(0,1).equals("v") ){
766                         String detail = " Bad Version (format) passed to getVerNumFromVerString: [" + versionString + "]."; 
767                         throw new AAIException("AAI_6121", detail); 
768                 }
769                 else {
770                         String intPart = versionString.substring(1,strLen);
771                         try {
772                                 versionInt = Integer.parseInt( intPart );
773                         }
774                         catch( Exception e ){
775                                 String detail = " Bad Version passed to getVerNumFromVerString: [" + versionString + "]."; 
776                                 throw new AAIException("AAI_6121", detail);
777                         }
778                 }
779                 return versionInt;
780         }
781
782         
783         /**
784          * Gets the node key prop names.
785          *
786          * @param transId the trans id
787          * @param fromAppId the from app id
788          * @param nodeType the node type
789          * @param apiVersion the api version
790          * @return HashMap of keyProperties
791          * @throws AAIException the AAI exception
792          */
793         public static Collection <String> getNodeKeyPropNames( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{
794         
795                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
796                 
797                 Collection <String> keyProps = new ArrayList <String>();
798                 if( dbMaps.NodeKeyProps.containsKey(nodeType) ){
799                         keyProps = dbMaps.NodeKeyProps.get(nodeType);
800                 }
801                 else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
802                         // The passed-in nodeType was really a nodeCategory, so we need to look up the key params
803                         Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType);
804                         Iterator <String> catItr = nTypeCatCol.iterator();
805                         String catInfo = "";
806                         if( catItr.hasNext() ){
807                                 // For now, we only look for one.
808                                 catInfo = catItr.next();
809                         }
810                         else {
811                                 String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP);
812                                 throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType+ " (ver=" + defVer + ")"); 
813                         }
814
815                         String [] flds = catInfo.split(",");
816                         if( flds.length != 4 ){
817                                 throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data for nodeType = [" + nodeType + "]."); 
818                         }
819
820                         String keyPropsString = flds[0];
821                         String [] propNames = keyPropsString.split("\\|");
822                         for( int i = 0; i < propNames.length; i++ ){
823                                 keyProps.add(propNames[i]);
824                         }
825                 }
826                 else {
827                         String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP);
828                         throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType+ " (ver=" + defVer + ")"); 
829                 }
830
831                 return keyProps;
832
833         }// end of getNodeKeyPropNames
834         
835         /**
836          * Gets the node key prop names.
837          *
838          * @param transId the trans id
839          * @param fromAppId the from app id
840          * @param nodeType the node type
841          * @return the node key prop names
842          * @throws AAIException the AAI exception
843          */
844         @Deprecated
845         public static Collection <String> getNodeKeyPropNames( String transId, String fromAppId, String nodeType ) throws AAIException{
846                 return getNodeKeyPropNames(  transId,  fromAppId,  nodeType, null);
847         }
848
849         /**
850          * Gets the node alt key 1 prop names.
851          *
852          * @param transId the trans id
853          * @param fromAppId the from app id
854          * @param nodeType the node type
855          * @param apiVersion the api version
856          * @return HashMap of keyProperties
857          * @throws AAIException the AAI exception
858          */
859         public static Collection <String> getNodeAltKey1PropNames( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{
860
861                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
862                 
863                 Collection <String> altKey1Props = new ArrayList <String>();
864                 if( dbMaps.NodeAltKey1Props.containsKey(nodeType) ){
865                         altKey1Props = dbMaps.NodeAltKey1Props.get(nodeType);
866                 }
867                 else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
868                         // The passed-in nodeType was really a nodeCategory, so we need to look up the key params
869                         Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType);
870                         Iterator <String> catItr = nTypeCatCol.iterator();
871                         String catInfo = "";
872                         if( catItr.hasNext() ){
873                                 catInfo = catItr.next();
874                                 String [] flds = catInfo.split(",");
875                                 if( flds.length != 4 ){
876                                         throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); 
877                                 }
878
879                                 String altKeyPropsString = flds[1];
880                                 String [] propNames = altKeyPropsString.split("\\|");
881                                 for( int i = 0; i < propNames.length; i++ ){
882                                         altKey1Props.add(propNames[i]);
883                                 }
884                         }
885                 }
886
887                 return altKey1Props;
888
889         }// end of getNodeAltKey1PropNames
890         
891         /**
892          * Gets the node alt key 1 prop names.
893          *
894          * @param transId the trans id
895          * @param fromAppId the from app id
896          * @param nodeType the node type
897          * @return the node alt key 1 prop names
898          * @throws AAIException the AAI exception
899          */
900         @Deprecated
901         public static Collection <String> getNodeAltKey1PropNames( String transId, String fromAppId, String nodeType ) throws AAIException{
902                 return getNodeAltKey1PropNames(  transId,  fromAppId,  nodeType, null);
903         }
904
905
906         /**
907          * Gets the unique node.
908          *
909          * @param transId the trans id
910          * @param fromAppId the from app id
911          * @param graph the graph
912          * @param nodeType the node type
913          * @param keyPropsHash the key props hash
914          * @param depNodeVal the dep node val
915          * @param apiVersion the api version
916          * @return TitanVertex
917          * @throws AAIException the AAI exception
918          */
919         @Deprecated
920         public static TitanVertex getUniqueNode( String transId, String fromAppId, TitanTransaction graph, String nodeType,
921                         HashMap<String,Object> keyPropsHash, TitanVertex depNodeVal, String apiVersion )         throws AAIException{
922                 
923                 // NOTE - this is really for use by the PersistNode method -- it is looking to see if
924                 //     a node exists in the database given either Primary or Alternate Key data and dependent
925                 //     node data (if required for uniqueness).
926
927                 // Note - the passed in nodeType could really be a nodeTypeCategory ---
928                 Boolean nodeTypeIsCategory = DbEdgeRules.NodeTypeCategory.containsKey(nodeType);
929
930                 Boolean useDepNode = false;
931                 if( needsADepNode4Uniqueness(transId, fromAppId, nodeType, apiVersion) ){
932                         // This kind of node depends on another node for uniqueness
933                         if( depNodeVal == null ){
934                                 // They should have passed in the node that this one depends on
935                                 throw new AAIException("AAI_6109", "null dependentNode object passed to getUniqueNode() but " + nodeType + " requires one."); 
936                         }
937                         else if( ! nodeTypeACanDependOnB(transId, fromAppId, nodeType, depNodeVal.<String>property("aai-node-type").orElse(null), apiVersion) ){        
938                                 // They should have passed in the right type of node as the dependent node
939                                 throw new AAIException("AAI_6109", "dependentNode of type " + depNodeVal.<String>property("aai-node-type").orElse(null) + " passed to getUniqueNode() for nodeType" + nodeType + ".\n"); 
940                         }
941                         useDepNode = true;
942                 }
943                 else {
944                         depNodeVal = null;
945                 }
946
947                 // We assume that all NodeTypes have at least one key-property defined.  A dependentNode is optional.
948                 // Note - instead of key-properties (the primary key properties), a user could pass
949                 //        alternate-key values if they are defined for the nodeType.
950                 ArrayList<String> kName = new ArrayList<String>();
951                 ArrayList<Object> kVal = new ArrayList<Object>();
952
953                 Collection <String> keyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion);
954                 Iterator <String> keyPropI = keyProps.iterator();
955                 Boolean haveSomePrimKeyProps = false;
956                 Boolean primaryKeyComplete = true;
957                 while( keyPropI.hasNext() ){
958                         haveSomePrimKeyProps = true;
959                         
960                         String propName = keyPropI.next();
961                         if( ! keyPropsHash.containsKey(propName) ){
962                                 primaryKeyComplete = false;
963                         }
964                         else {
965                                 Object valObj = keyPropsHash.get(propName);
966                                 if( valObj == null ){
967                                         primaryKeyComplete = false;
968                                 }
969                                 else {
970                                         String value = valObj.toString();
971                                         if( value == null || value.equals("") ){
972                                                 // They passed the property name, but no value
973                                                 primaryKeyComplete = false;
974                                         }
975                                 }
976                         }
977                 }
978                 
979                 int i = -1;
980                 if( haveSomePrimKeyProps && primaryKeyComplete ){
981                         keyPropI = keyProps.iterator();
982                         while( keyPropI.hasNext() ){
983                                 String propName = keyPropI.next();
984                                 String value = (keyPropsHash.get(propName)).toString();
985                                 i++;
986                                 kName.add(i, propName);
987                                 kVal.add(i, (Object)value);
988                         }
989                 }
990                 else {
991                         // See if they're using the alternate key
992                         Collection <String> altKey1Props = getNodeAltKey1PropNames(transId, fromAppId, nodeType, apiVersion);
993                         Iterator <String> altKey1PropI = altKey1Props.iterator();
994                         Boolean haveSomeAltKey1Props = false;
995                         Boolean altKey1Complete = true;
996                         while( altKey1PropI.hasNext() ){
997                                 haveSomeAltKey1Props = true;
998                                 String propName = altKey1PropI.next();
999                                 if( ! keyPropsHash.containsKey(propName) ){
1000                                         altKey1Complete = false;
1001                                 }
1002                                 else {
1003                                         Object valObj = keyPropsHash.get(propName);
1004                                         if( valObj == null ){
1005                                                 altKey1Complete = false;
1006                                         }
1007                                         else {
1008                                                 String value = valObj.toString();
1009                                                 if( value == null || value.equals("") ){
1010                                                         // They passed the property name, but no value
1011                                                         altKey1Complete = false;
1012                                                 }
1013                                         }
1014                                 }
1015                         }
1016                         if( haveSomeAltKey1Props && altKey1Complete ){
1017                                 altKey1PropI = altKey1Props.iterator();
1018                                 while( altKey1PropI.hasNext() ){
1019                                         String propName = altKey1PropI.next();
1020                                         String value = (keyPropsHash.get(propName)).toString();
1021                                         i++;
1022                                         kName.add(i, propName);
1023                                         kVal.add(i, (Object)value);
1024                                 }
1025                         }
1026                 }
1027
1028                 int topPropIndex = i;
1029                 TitanVertex tiV = null;
1030                 String propsAndValuesForMsg = "";
1031                 if( !useDepNode ){ 
1032                         // There is no node that this type of node depends on, so we can look for it based 
1033                         //    solely on the Aai-defined key fields.
1034                         Iterable <?> verts = null;
1035
1036                         if( topPropIndex == -1 ){
1037                                 // Problem if no key Properties defined for this nodeType  
1038                                 String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP);
1039                                 throw new AAIException("AAI_6105", "Bad or Incomplete Key Property params: (" + keyPropsHash.toString() + 
1040                                                 ") for nodeType: " + nodeType + " (ver=" + defVer + ")"); 
1041                         }
1042                         else if( topPropIndex == 0 ){
1043                                 if (nodeTypeIsCategory) // dont know real type
1044                                         verts= graph.query().has(kName.get(0),kVal.get(0)).vertices();
1045                                 else // need this to find dvs switch: dvs.switch-name and port-group.switch-name issue
1046                                         verts= graph.query().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType).vertices();
1047                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") ";
1048                         }       
1049                         else if( topPropIndex == 1 ){
1050                                 verts =  graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).vertices();
1051                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1052                                                 + kName.get(1) + " = " + kVal.get(1) + ") ";
1053                         }                       
1054                         else if( topPropIndex == 2 ){
1055                                 verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).vertices();
1056                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1057                                                 + kName.get(1) + " = " + kVal.get(1) + ", " 
1058                                                 + kName.get(2) + " = " + kVal.get(2) +  ") ";
1059                         }       
1060                         else if( topPropIndex == 3 ){
1061                                 verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).vertices();
1062                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1063                                                 + kName.get(1) + " = " + kVal.get(1) + ", " 
1064                                                 + kName.get(2) + " = " + kVal.get(2) + ", " 
1065                                                 + kName.get(3) + " = " + kVal.get(3) +  ") ";
1066                         }                       
1067                         else {
1068                                 String emsg = " We only support 4 keys per nodeType for now \n";
1069                                 throw new AAIException("AAI_6114", emsg); 
1070                         }
1071
1072                         Iterator <?> vertI = verts.iterator();
1073                         if( vertI != null && vertI.hasNext()) {
1074                                 // We found a vertex that meets the input criteria. 
1075                                 tiV = (TitanVertex) vertI.next();
1076
1077                                 if( vertI.hasNext() ){
1078                                         // Since this routine is looking for a unique node for the given input values, if  
1079                                         // more than one is found - it's a problem.
1080                                         throw new AAIException("AAI_6112", "More than one Node found by getUniqueNode for params: " + propsAndValuesForMsg); 
1081                                 }
1082                         } 
1083                         else {
1084                                 // No Vertex was found for this key - throw a not-found exception
1085                                 throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg);
1086                         }
1087                 }
1088                 else {
1089                         // Need to use the dependent vertex to look for this one.
1090                         // filter this to the actual keys because
1091                         HashMap<String,Object> onlyKeysHash = new HashMap<String,Object>();
1092                         
1093                         Collection <String> onlyKeyProps = getNodeKeyPropNames(transId, fromAppId, nodeType, apiVersion);
1094                         
1095                         Iterator <String> onlyKeyPropsI = onlyKeyProps.iterator();
1096                         
1097                         while( onlyKeyPropsI.hasNext() ){
1098                                 String keyName = onlyKeyPropsI.next();
1099                                 onlyKeysHash.put(keyName, keyPropsHash.get(keyName));
1100                         }
1101
1102                         propsAndValuesForMsg = onlyKeysHash.toString() + " combined with a Dependent [" + depNodeVal.<String>property("aai-node-type").orElse(null) + "] node."; 
1103                         ArrayList<TitanVertex> resultList = DbMeth.getConnectedNodes(transId, fromAppId, graph, nodeType, onlyKeysHash, 
1104                                         depNodeVal, apiVersion, false);
1105                         if( resultList.size() > 1 ){
1106                                 // More than one vertex found when we thought there should only be one.
1107                                 throw new AAIException("AAI_6112", "More than one Node found by getUniqueNode for params: " + propsAndValuesForMsg);  
1108                         }
1109                         else if( resultList.size() == 1 ){
1110                                 tiV = resultList.get(0);
1111                         }
1112                 }
1113
1114                 if( tiV == null ){
1115                         // No Vertex was found for this key - throw a not-found exception
1116                         throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg);
1117                 }
1118                 else {
1119                         if( !DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
1120                                 // The nodeType passed in was a real one, not a nodeTypeCategory, so we will
1121                                 // use it as part of the query to make sure we find the right type of node.
1122                                 // This can be an issue if they're using nodeTypes covered by a nodeTypeCategory but
1123                                 // pass in the wrong nodeType.  We don't want them to ask for one thing and get the other.
1124                                 String foundNodeType = tiV.<String>property("aai-node-type").orElse(null);
1125                                 if( foundNodeType != null && !foundNodeType.equals(nodeType) ){
1126                                         throw new AAIException("AAI_6114", "No Node of type " + nodeType + " found for properties: " + propsAndValuesForMsg + " (did find a " + foundNodeType + " though.)");
1127                                 }
1128                         }
1129                         
1130                         return tiV;
1131                 }
1132
1133         }// End of getUniqueNode() 
1134
1135         /**
1136          * Gets the unique node.
1137          *
1138          * @param transId the trans id
1139          * @param fromAppId the from app id
1140          * @param graph the graph
1141          * @param nodeType the node type
1142          * @param keyPropsHash the key props hash
1143          * @param depNodeVal the dep node val
1144          * @return the unique node
1145          * @throws AAIException the AAI exception
1146          */
1147         @Deprecated
1148         public static TitanVertex getUniqueNode( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1149                         HashMap<String,Object> keyPropsHash, TitanVertex depNodeVal)     throws AAIException {
1150                 return getUniqueNode( transId, fromAppId, graph, nodeType,
1151                                 keyPropsHash,  depNodeVal, null );
1152         }
1153         // End getUniqueNode()
1154
1155
1156         /**
1157          * Gets the unique node with dep params.
1158          *
1159          * @param transId the trans id
1160          * @param fromAppId the from app id
1161          * @param graph the graph
1162          * @param nodeType the node type
1163          * @param nodePropsHash the node props hash
1164          * @param apiVersion the api version
1165          * @return TitanVertex
1166          * @throws AAIException the AAI exception
1167          */
1168         @Deprecated
1169         public static TitanVertex getUniqueNodeWithDepParams( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1170                         HashMap<String,Object> nodePropsHash, String apiVersion ) 
1171                                         throws AAIException{
1172                 /*
1173                  * This method uses the nodePropsHash to walk back over dependent nodes until it finds one that
1174                  * does not depend on any other for uniqueness.   It uses the getUniqueNode() method as it finds
1175                  * dependent nodes.   NOTE -- it is passed a hash of all the nodeProperties -- for itself and
1176                  * for any dependent nodes that it will need to find.   There are some types of nodes that can
1177                  * depend on more than one node, we assume that there wouldn't be a case where BOTH types of
1178                  * dependent nodes are in the trail that we need to traverse.  Ie. an ipaddress can depend on
1179                  * either a vserver or pserver.  NOTE this case can now happen -- nodePropsHash
1180                  * should now be sent as a LinkedHashMap in this case so we can search in order. 
1181                  */
1182
1183                 // NOTE ALSO -- We're currently supporting 6 layers of dependency.   We never thought there would be this
1184                 //       many layers before hitting a node-type that would be uniquely identifiable on it's own.   So the
1185                 //       code is a little ugly with all these nested if-then-else's.   Since we're supporting so many
1186                 //       layers, it should be re-written so we can support "n" layers instead of having to go in hear
1187                 //       and adding code...   But as of 15-07, we really don't NEED more than 5.
1188
1189                 // NOTE: The passed in nodeType could really be a nodeTypeCategory -- 
1190                 //       The calls to figureDepNodeTypeForRequest() below will deal with it for the dep nodes, the 
1191                 //       call to getUniqueNode() takes care of it for the node itself.
1192
1193                 TitanVertex nullVert = null;
1194                 String depNodeType = figureDepNodeTypeForRequest( transId, fromAppId, nodeType, nodePropsHash, apiVersion );
1195                 if( depNodeType.equals("")){
1196                         // This kind of node does not depend on another node for uniqueness, so 
1197                         // we can just use the "getUniqueNode()" method to get it.
1198                         HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1199                         return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, nullVert, apiVersion) );
1200                 }
1201                 else {
1202                         // Will need to find the second-layer dependent node
1203                         String secondLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, depNodeType, nodePropsHash, apiVersion );
1204                         if( secondLayerDepNodeType.equals("")){
1205                                 // This second-layer kind of node does not depend on another node for uniqueness.
1206                                 // So once we find the second-layer node, we can use it to get the top-layer guy.
1207                                 HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion);
1208                                 TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, nullVert, apiVersion);
1209
1210                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1211                                 return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) );
1212                         }
1213                         else {
1214                                 // Will need to find the third-layer dependent node
1215                                 ///  String thirdLayerDepNodeType = dbMaps.NodeDependencies.get(secondLayerDepNodeType);
1216                                 String thirdLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion );
1217
1218                                 if( thirdLayerDepNodeType.equals("")){
1219                                         // This third-layer kind of node does not depend on another node for uniqueness.
1220                                         // So we can find it, and then use it to find the second-layer and then use that to find the top guy.
1221                                         HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion);
1222                                         TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion);
1223
1224                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion);
1225                                         TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion);
1226
1227                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1228
1229                                         return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) );
1230                                 }
1231                                 else {
1232                                         // Will need to find the third-layer dependent node
1233                                         String forthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion );
1234                                         if( forthLayerDepNodeType == null || forthLayerDepNodeType.equals("")){
1235                                                 // This forth-layer kind of node does not depend on another node for uniqueness.
1236                                                 // So we can find it, and then use it to find the third, then second-layer and then use that to find the top guy.
1237                                                 HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion);
1238                                                 TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion);
1239
1240                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion);
1241                                                 TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion);
1242
1243                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion);
1244                                                 TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion);
1245
1246                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1247                                                 return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) );
1248                                         }
1249                                         else {
1250                                                 // Will need to find the forth-layer dependent node
1251                                                 String fifthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion );
1252                                                 if( fifthLayerDepNodeType == null || fifthLayerDepNodeType.equals("")){
1253                                                         // This fifth-layer kind of node does not depend on another node for uniqueness.
1254                                                         // So we can find it, and then use it to find the forth, third, then second-layer and then use that to find the top guy.
1255                                                         HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion);
1256                                                         TitanVertex fifthLayerDepVert = getUniqueNode(transId, fromAppId, graph, forthLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion);
1257
1258                                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion);
1259                                                         TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, fifthLayerDepVert, apiVersion);
1260
1261                                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion);
1262                                                         TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion);
1263
1264                                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion);
1265                                                         TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion);
1266
1267                                                         thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1268                                                         return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) );
1269                                                 }
1270                                                 else {
1271                                                         // Will need to find the fifth-layer dependent node
1272                                                         String sixthLayerDepNodeType = figureDepNodeTypeForRequest( transId, fromAppId, fifthLayerDepNodeType, nodePropsHash, apiVersion );
1273                                                         if( sixthLayerDepNodeType == null || sixthLayerDepNodeType.equals("")){
1274                                                                 // This six-layer kind of node does not depend on another node for uniqueness.
1275                                                                 // So we can find it, and then use it to find the fifth, forth, third, then second-layer and then use that to find the top guy.
1276                                                                 HashMap <String,Object> thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, fifthLayerDepNodeType, nodePropsHash, apiVersion);
1277                                                                 TitanVertex sixthLayerDepVert = getUniqueNode(transId, fromAppId, graph, fifthLayerDepNodeType, thisNodeTypeParamHash, nullVert, apiVersion);
1278
1279                                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, forthLayerDepNodeType, nodePropsHash, apiVersion);
1280                                                                 TitanVertex fifthLayerDepVert = getUniqueNode(transId, fromAppId, graph, forthLayerDepNodeType, thisNodeTypeParamHash, sixthLayerDepVert, apiVersion);
1281
1282                                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, thirdLayerDepNodeType, nodePropsHash, apiVersion);
1283                                                                 TitanVertex forthLayerDepVert = getUniqueNode(transId, fromAppId, graph, thirdLayerDepNodeType, thisNodeTypeParamHash, fifthLayerDepVert, apiVersion);
1284
1285                                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, secondLayerDepNodeType, nodePropsHash, apiVersion);
1286                                                                 TitanVertex thirdLayerDepVert = getUniqueNode(transId, fromAppId, graph, secondLayerDepNodeType, thisNodeTypeParamHash, forthLayerDepVert, apiVersion);
1287
1288                                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, depNodeType, nodePropsHash, apiVersion);
1289                                                                 TitanVertex secLayerDepVert = getUniqueNode(transId, fromAppId, graph, depNodeType, thisNodeTypeParamHash, thirdLayerDepVert, apiVersion);
1290
1291                                                                 thisNodeTypeParamHash = getThisNodeTypeParams(transId, fromAppId, nodeType, nodePropsHash, apiVersion);
1292                                                                 return( getUniqueNode(transId, fromAppId, graph, nodeType, thisNodeTypeParamHash, secLayerDepVert, apiVersion) );
1293                                                         }
1294                                                         else {
1295                                                                 // We don't currently support more layers.  We can later if we need to.
1296                                                                 // Hopefully, we'll never need to go this deep -- there should be unique keys in there somewhere! 
1297                                                                 throw new AAIException("AAI_6114", "CODE-LIMITATION - Can't resolve dependant node layers for nodeType = " + nodeType); 
1298                                                         }
1299                                                 }
1300                                         }
1301                                 }
1302                         }
1303                 }
1304         } // End getUniqueNodeWithDepParams()
1305         
1306         /**
1307          * Gets the unique node with dep params.
1308          *
1309          * @param transId the trans id
1310          * @param fromAppId the from app id
1311          * @param graph the graph
1312          * @param nodeType the node type
1313          * @param nodePropsHash the node props hash
1314          * @return the unique node with dep params
1315          * @throws AAIException the AAI exception
1316          */
1317         @Deprecated
1318         public static TitanVertex getUniqueNodeWithDepParams( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1319                         HashMap<String,Object> nodePropsHash ) throws AAIException {
1320                 return getUniqueNodeWithDepParams(transId, fromAppId, graph, nodeType, nodePropsHash, null);
1321         }
1322
1323
1324         /**
1325          * Gets the this node type params.
1326          *
1327          * @param transId the trans id
1328          * @param fromAppId the from app id
1329          * @param targetNodeType the target node type
1330          * @param passedHash the passed hash
1331          * @param apiVersion the api version
1332          * @return the this node type params
1333          * @throws AAIException the AAI exception
1334          */
1335         private static HashMap <String, Object> getThisNodeTypeParams(String transId, String fromAppId, String targetNodeType, 
1336                         HashMap<String,Object> passedHash, String apiVersion )throws AAIException{
1337                 /*
1338                  * For the passed-in hash, each key is assumed to look like, "nodeType.paramName".  We want to 
1339                  * pick out the entries that match the targetNodeType and return those with the values they go with.  The
1340                  * returned keys will have the "nodeType." stripped off.   
1341                  * 
1342                  * NOTE  - the nodeType passed to this method could actually be a nodeTypeCategory.  Just keepin it ugly.
1343                  */
1344
1345                 if( passedHash == null ){
1346                         throw new AAIException("AAI_6120", "Bad param:  null passedHash "); 
1347                 }
1348
1349                 String targetNodeTypeCat = "";
1350                 if( DbEdgeRules.NodeTypeCatMap.containsKey(targetNodeType) ){
1351                         targetNodeTypeCat = DbEdgeRules.NodeTypeCatMap.get(targetNodeType);
1352                 }
1353
1354                 HashMap <String,Object> returnHash = new HashMap <String,Object> ();
1355                 Iterator <Map.Entry<String,Object>>it = passedHash.entrySet().iterator();
1356                 while( it.hasNext() ){
1357                         Map.Entry <String,Object>pairs = (Map.Entry<String,Object>)it.next();
1358                         String k = (pairs.getKey()).toString();
1359                         int periodLoc = k.indexOf(".");
1360                         if( periodLoc <= 0 ){
1361                                 throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + k + "].  Expected format = [nodeName.paramName]\n"); 
1362                         }
1363                         else {
1364                                 String nty = k.substring(0,periodLoc);
1365                                 String paramName = k.substring(periodLoc + 1);
1366                                 if( nty.equals(targetNodeType) || (!targetNodeTypeCat.equals("") && nty.equals(targetNodeTypeCat)) ){
1367                                         String newK = paramName;
1368                                         returnHash.put( newK,pairs.getValue() );
1369                                 }
1370                         }
1371                 }
1372
1373                 //aaiLogger.debug(logline, " - end ");
1374                 return returnHash;
1375
1376         }// End of getThisNodeTypeParams()
1377         
1378         /**
1379          * Gets the this node type params.
1380          *
1381          * @param transId the trans id
1382          * @param fromAppId the from app id
1383          * @param targetNodeType the target node type
1384          * @param passedHash the passed hash
1385          * @return the this node type params
1386          * @throws AAIException the AAI exception
1387          */
1388         @Deprecated
1389         private static HashMap <String, Object> getThisNodeTypeParams(String transId, String fromAppId, String targetNodeType, 
1390                         HashMap<String,Object> passedHash )throws AAIException{
1391                 return getThisNodeTypeParams( transId,  fromAppId, targetNodeType, 
1392                                  passedHash, null);
1393                 
1394         }
1395
1396         /**
1397          * Gets the dep node types.
1398          *
1399          * @param transId the trans id
1400          * @param fromAppId the from app id
1401          * @param nodeType the node type
1402          * @param apiVersion the api version
1403          * @return the dep node types
1404          * @throws AAIException the AAI exception
1405          */
1406         public static ArrayList <String> getDepNodeTypes(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{
1407                 /*
1408                  * This returns any nodeTypes that this nodeType can be dependent on.  A particular instance of a node will only
1409                  * depend on one other node - we don't currently support dependence on multiple nodes.
1410                  */
1411                         
1412                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
1413
1414                 ArrayList <String> depNodeTypeL = new ArrayList <String> ();
1415                 if( !DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
1416                         // This is a good-ole nodeType
1417                         Collection <String> depNTColl =  dbMaps.NodeDependencies.get(nodeType);
1418                         Iterator <String> ntItr = depNTColl.iterator();
1419                         while( ntItr.hasNext() ){
1420                                 depNodeTypeL.add(ntItr.next());
1421                         }
1422                 }
1423                 else {
1424                         // The passed-in nodeType must really be a nodeTypeCategory
1425                         Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType);
1426                         Iterator <String> catItr = nTypeCatCol.iterator();
1427                         String catInfo = "";
1428                         if( catItr.hasNext() ){
1429                                 // For now, we only look for one.
1430                                 catInfo = catItr.next();
1431                         }
1432                         else {
1433                                 throw new AAIException("AAI_6121", "Error getting DbEdgeRules.NodeTypeCategory info for nodeTypeCat = " + nodeType); 
1434                         }
1435
1436                         String [] flds = catInfo.split(",");
1437                         if( flds.length != 4 ){
1438                                 throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); 
1439                         }
1440
1441                         String nodeTypesString = flds[0];
1442                         String  hasDepNodes = flds[3];
1443                         if( hasDepNodes.equals("true") ){
1444                                 String [] ntNames = nodeTypesString.split("\\|");
1445                                 for( int i = 0; i < ntNames.length; i++ ){
1446                                         Collection <String> depNTColl =  dbMaps.NodeDependencies.get(nodeType);
1447                                         Iterator <String> ntItr = depNTColl.iterator();
1448                                         while( ntItr.hasNext() ){
1449                                                 String depNode = ntItr.next();
1450                                                 if( !depNodeTypeL.contains(depNode) ){
1451                                                         depNodeTypeL.add(depNode);
1452                                                 }
1453                                         }
1454                                 }       
1455                         }
1456                 }
1457
1458
1459                 return depNodeTypeL;
1460
1461         }// End getDepNodeTypes()
1462         
1463         /**
1464          * Gets the dep node types.
1465          *
1466          * @param transId the trans id
1467          * @param fromAppId the from app id
1468          * @param nodeType the node type
1469          * @return the dep node types
1470          * @throws AAIException the AAI exception
1471          */
1472         @Deprecated
1473         public static ArrayList <String> getDepNodeTypes(String transId, String fromAppId, String nodeType)throws AAIException{
1474                 return getDepNodeTypes( transId,  fromAppId,  nodeType, null);
1475         }
1476
1477         /**
1478          * Gets the default delete scope.
1479          *
1480          * @param transId the trans id
1481          * @param fromAppId the from app id
1482          * @param nodeType the node type
1483          * @param apiVersion the api version
1484          * @return the default delete scope
1485          * @throws AAIException the AAI exception
1486          */
1487         private static String getDefaultDeleteScope(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{
1488
1489                 // At some point we may have different delete rules for different services, so this is
1490                 // a list for now even thought there's only one scope per nodeType.
1491                 Collection <String> scopeList = DbEdgeRules.DefaultDeleteScope.get(nodeType);
1492                 if( scopeList.isEmpty() ){
1493                         throw new AAIException("AAI_6121", "No default deleteScope found for nodeType = [" + nodeType + "] "); 
1494                 }
1495                 else {
1496                         Iterator <String> ito = scopeList.iterator();
1497                         return ito.next();
1498                 }
1499
1500         }// End getDefaultDeleteScope()
1501         
1502         /**
1503          * Gets the default delete scope.
1504          *
1505          * @param transId the trans id
1506          * @param fromAppId the from app id
1507          * @param nodeType the node type
1508          * @return the default delete scope
1509          * @throws AAIException the AAI exception
1510          */
1511         @Deprecated
1512         private static String getDefaultDeleteScope(String transId, String fromAppId, String nodeType)throws AAIException{
1513                 return getDefaultDeleteScope( transId,  fromAppId,  nodeType, null);
1514         }
1515
1516         /**
1517          * Needs A dep node 4 uniqueness.
1518          *
1519          * @param transId the trans id
1520          * @param fromAppId the from app id
1521          * @param nodeType the node type
1522          * @param apiVersion the api version
1523          * @return the boolean
1524          * @throws AAIException the AAI exception
1525          */
1526         public static Boolean needsADepNode4Uniqueness(String transId, String fromAppId, String nodeType, String apiVersion)throws AAIException{
1527                 // Note: the passed in nodeType could really be a nodeTypeCategory.  That is handled by getDepNodeTypes()
1528
1529                 ArrayList <String> depList = getDepNodeTypes(transId, fromAppId, nodeType, apiVersion);
1530                 if( depList.isEmpty() ){
1531                         return false;
1532                 }
1533                 else {
1534                         return true;
1535                 }
1536
1537         }// End needsADepNode4Uniqueness()
1538         
1539         /**
1540          * Needs A dep node 4 uniqueness.
1541          *
1542          * @param transId the trans id
1543          * @param fromAppId the from app id
1544          * @param nodeType the node type
1545          * @return the boolean
1546          * @throws AAIException the AAI exception
1547          */
1548         @Deprecated
1549         private static Boolean needsADepNode4Uniqueness(String transId, String fromAppId, String nodeType)throws AAIException{
1550                 return needsADepNode4Uniqueness( transId,  fromAppId,  nodeType,  null);
1551         }
1552
1553         /**
1554          * Node type A can depend on B.
1555          *
1556          * @param transId the trans id
1557          * @param fromAppId the from app id
1558          * @param nodeTypeA the node type A
1559          * @param nodeTypeB the node type B
1560          * @param apiVersion the api version
1561          * @return the boolean
1562          * @throws AAIException the AAI exception
1563          */
1564         public static Boolean nodeTypeACanDependOnB(String transId, String fromAppId, String nodeTypeA, String nodeTypeB, String apiVersion)
1565                         throws AAIException{
1566                 // Note: the passed in nodeType could really be a nodeTypeCategory.  That is handled by getDepNodeTypes()
1567
1568                 ArrayList <String> depList = getDepNodeTypes(transId, fromAppId, nodeTypeA, apiVersion);
1569                 if( depList.isEmpty() ){
1570                         return false;
1571                 }
1572                 else if( depList.contains(nodeTypeB) ){
1573                         return true;
1574                 }
1575                 else { 
1576                         return false;
1577                 }
1578
1579         }// End nodeTypeACanDependOnB()
1580         
1581         /**
1582          * Node type A can depend on B.
1583          *
1584          * @param transId the trans id
1585          * @param fromAppId the from app id
1586          * @param nodeTypeA the node type A
1587          * @param nodeTypeB the node type B
1588          * @return the boolean
1589          * @throws AAIException the AAI exception
1590          */
1591         @Deprecated
1592         private static Boolean nodeTypeACanDependOnB(String transId, String fromAppId, String nodeTypeA, String nodeTypeB)
1593                         throws AAIException{
1594                 return nodeTypeACanDependOnB( transId,  fromAppId,  nodeTypeA,  nodeTypeB, null);
1595         }
1596
1597         /**
1598          * Figure dep node type for request.
1599          *
1600          * @param transId the trans id
1601          * @param fromAppId the from app id
1602          * @param nodeType the node type
1603          * @param requestParamHash the request param hash
1604          * @param apiVersion the api version
1605          * @return the string
1606          * @throws AAIException the AAI exception
1607          */
1608         public static String figureDepNodeTypeForRequest(String transId, String fromAppId, String nodeType, 
1609                         HashMap<String,Object> requestParamHash, String apiVersion )throws AAIException{
1610                 /*
1611                  * This is ugly.   But if the passed-in nodeType is dependent on another nodeType for 
1612                  * uniqueness, we need to return what that dependent node-type is.  The ugly comes in
1613                  * because a node can be dependent on more than one type of node.   So, to tell which one
1614                  * is going to apply, we root through the passed request parameters to see which of 
1615                  * the possible dependent node types is being used.
1616                  * Note -- if there comes a day when there are so many dependencies that the request could
1617                  * have more than one that match -- Then we need to think up something new.   But for now,
1618                  * we have to assume that if there are more than one legal dep-node-types, only one will
1619                  * be represented in the requestHash data.  >>> NOTE >>> That day has come.  For
1620                  * the upstreamers will send in a LinkedHashMap instead of just an unordered
1621                  * HashMap so we can look in order for the dependent node.
1622                  * 
1623                  */
1624
1625                 if( requestParamHash == null ){
1626                         throw new AAIException("AAI_6120", "Bad param:  null requestParamHash "); 
1627                 }
1628
1629                 ArrayList <String> depNodeTypes = getDepNodeTypes(transId, fromAppId, nodeType, apiVersion);
1630                 if( depNodeTypes.isEmpty() ){
1631                         // This kind of node is not dependent on any other
1632                         //aaiLogger.debug(logline, " (not dependent) - end ");
1633                         return "";
1634                 }
1635                 else if( depNodeTypes.size() == 1 ){
1636                         // This kind of node can only depend on one other nodeType - so return that.
1637                         //aaiLogger.debug(logline, " (depends on " + depNodeTypes.get(0) + " - end ");
1638                         return depNodeTypes.get(0);
1639                 }
1640                 else {
1641                         // We need to look to find the first of the dep-node types that is represented in the passed-in
1642                         // request data.  That will be the one we need to use.
1643
1644                         // first find out what node-types are represented in the requestHash
1645                         
1646                         Iterator <Map.Entry<String,Object>>it = requestParamHash.entrySet().iterator();
1647                         while( it.hasNext() ){
1648                                 Map.Entry <String,Object>pairs = (Map.Entry<String,Object>)it.next();
1649                                 String k = (pairs.getKey()).toString();
1650                                 int periodLoc = k.indexOf(".");
1651                                 if( periodLoc <= 0 ){
1652                                         throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + k + "].  Expected format = [nodeName.paramName]\n"); 
1653                                 }
1654                                 else {
1655                                         String nty = k.substring(0,periodLoc);
1656                                         if( depNodeTypes.contains(nty) ){
1657                                                 // This is the first possible dep. node type we've found for the passed in data set
1658                                                 return nty;
1659                                         }
1660                                 }
1661                         }
1662
1663                 }
1664
1665                 // It's not an error if none is found - the caller needs to deal with cases where there
1666                 // should be a dep. node identified but isn't.
1667                 //aaiLogger.debug(logline, " no dep NT found - end ");
1668                 return "";
1669
1670         }// End of figureDepNodeTypeForRequest()
1671         
1672         /**
1673          * Figure dep node type for request.
1674          *
1675          * @param transId the trans id
1676          * @param fromAppId the from app id
1677          * @param nodeType the node type
1678          * @param requestParamHash the request param hash
1679          * @return the string
1680          * @throws AAIException the AAI exception
1681          */
1682         @Deprecated
1683         public static String figureDepNodeTypeForRequest(String transId, String fromAppId, String nodeType, 
1684                         HashMap<String,Object> requestParamHash )throws AAIException{
1685                  return figureDepNodeTypeForRequest( transId,  fromAppId,  nodeType, requestParamHash, null);
1686         }
1687         
1688         /**
1689          * Detach connected nodes.
1690          *
1691          * @param transId the trans id
1692          * @param fromAppId the from app id
1693          * @param graph the graph
1694          * @param nodeType the node type
1695          * @param propFilterHash the prop filter hash
1696          * @param startNodeVal the start node val
1697          * @param autoDeleteOrphans the auto delete orphans
1698          * @param apiVersion the api version
1699          * @return deletedNodeCount
1700          * @throws AAIException the AAI exception
1701          */
1702         public static int detachConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1703                         HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, boolean autoDeleteOrphans, String apiVersion ) throws AAIException{
1704
1705                 /*  Find nodes that are attached to this node which meet the nodeType/filterParams criteria.
1706                  *  Remove the edges that go to those nodes.
1707                  *  If that turns any of the nodes into an orphan, then delete it if the autoDeleteOrphans flag is set.
1708                  *  Return a count of how many nodes were actually deleted (not just detached).
1709                  */
1710
1711                 int deletedCount = 0;
1712
1713                 if( startNodeVal == null ){
1714                         // They should have passed in the node that this query starts from
1715                         throw new AAIException("AAI_6109", "null startNode object passed to detachConnectedNodes()."); 
1716                 }
1717
1718                 // We want to loop through the connected Nodes that we found.
1719                 // For each connected Node, we'll get the all edges that start from that node and look for the one
1720                 //     that connects back to our startNode.
1721                 // Only delete the edge that connects back to our startNode.
1722                 // then autoDeleteOrphans flag is set, then delete the connectedNode if it's now orphaned.
1723                 //      
1724
1725                 String startNodeVId =  startNodeVal.id().toString();
1726                 ArrayList<TitanVertex> conNodeList = getConnectedNodes( transId, fromAppId, graph, nodeType, propFilterHash, startNodeVal, apiVersion, false );
1727                 Iterator<TitanVertex> conVIter = conNodeList.iterator();
1728                 while( conVIter.hasNext() ){            
1729                         TitanVertex connectedVert = conVIter.next();
1730                         boolean isFirstOne = true;
1731                         Iterator<Edge> eI = connectedVert.edges(Direction.BOTH);
1732                         while( eI.hasNext() ){
1733                                 TitanEdge ed = (TitanEdge) eI.next();
1734                                 TitanVertex otherVtx = (TitanVertex) ed.otherVertex(connectedVert);
1735                                 String otherSideLookingBackVId =  otherVtx.id().toString();
1736                                 if( startNodeVId.equals(otherSideLookingBackVId) ){
1737                                         // This is an edge from the connected node back to our starting node
1738                                         if( isFirstOne && !eI.hasNext() && autoDeleteOrphans ){
1739                                                 // This was the one and only edge for this connectedNode, so 
1740                                                 // delete the node and edge since flag was set 
1741                                                 String resVers = connectedVert.<String>property("resource-version").orElse(null);
1742                                                 removeAaiNode( transId, fromAppId, graph, connectedVert,  "USE_DEFAULT", apiVersion, resVers);
1743                                                 deletedCount = deletedCount + 1;
1744                                         }
1745                                         else {
1746                                                 removeAaiEdge( transId, fromAppId, graph, ed );
1747                                         }
1748                                 }
1749                                 isFirstOne = false;
1750                         }
1751                 }
1752                 return deletedCount;
1753
1754         } // end of detachConnectedNodes()
1755
1756
1757
1758         /**
1759          * Detach connected nodes.
1760          *
1761          * @param transId the trans id
1762          * @param fromAppId the from app id
1763          * @param graph the graph
1764          * @param nodeType the node type
1765          * @param propFilterHash the prop filter hash
1766          * @param startNodeVal the start node val
1767          * @param autoDeleteOrphans the auto delete orphans
1768          * @return the int
1769          * @throws AAIException the AAI exception
1770          */
1771         @Deprecated
1772         public static int detachConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1773                         HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, boolean autoDeleteOrphans ) throws AAIException{
1774                 return detachConnectedNodes(  transId,  fromAppId,  graph,  nodeType,
1775                                 propFilterHash,  startNodeVal,  autoDeleteOrphans, null);
1776         }
1777         
1778         /**
1779          * Gets the nodes.
1780          *
1781          * @param transId the trans id
1782          * @param fromAppId the from app id
1783          * @param graph the graph
1784          * @param nodeType the node type
1785          * @param propFilterHash the prop filter hash
1786          * @param noFilterOnPurpose the no filter on purpose
1787          * @param apiVersion the api version
1788          * @return ArrayList<TitanVertex>
1789          * @throws AAIException the AAI exception
1790          */
1791         @Deprecated
1792         public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1793                         HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose, String apiVersion ) throws AAIException{
1794                 boolean skipGroomingFlag = true; 
1795                 // we will only do real-time grooming if a system variable is set, telling us not to skip it.
1796                 String skipGroomingStr = AAIConstants.AAI_SKIPREALTIME_GROOMING;
1797                 if( skipGroomingStr.equals("false") ){
1798                         skipGroomingFlag = false;
1799                 }
1800                 return( getNodes(transId, fromAppId, graph, nodeType, propFilterHash, noFilterOnPurpose, apiVersion, skipGroomingFlag) );
1801         }
1802         
1803         /**
1804          * Gets the nodes.
1805          *
1806          * @param transId the trans id
1807          * @param fromAppId the from app id
1808          * @param graph the graph
1809          * @param nodeType the node type
1810          * @param propFilterHash the prop filter hash
1811          * @param noFilterOnPurpose the no filter on purpose
1812          * @param apiVersion the api version
1813          * @param skipGroomCheck the skip groom check
1814          * @return ArrayList<TitanVertex>
1815          * @throws AAIException the AAI exception
1816          */
1817         @Deprecated
1818         public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1819                         HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose, String apiVersion, boolean skipGroomCheck ) 
1820                                         throws AAIException{
1821                 //  Note - the skipGroomCheck flag is set to true when the DataGrooming tool is using this method to collect
1822                 //     node data.  When the grooming tool is collecting data, we don't want any nodes skipped, because we
1823                 //     want details about what nodes/edges are bad - more detail than the check in this method does
1824                 //     as it checks if a node is ok to return to a caller.
1825                 
1826                 /* Use the nodeType + filterParams to find nodes.    
1827                  */
1828                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
1829                 
1830                 ArrayList<TitanVertex> returnVertList = new ArrayList<TitanVertex>();
1831                 if( nodeType == null || nodeType.equals("") ){
1832                         // They should have passed in a nodeType
1833                         throw new AAIException("AAI_6118", "Required field: nodeType not passed to getNodes()."); 
1834                 }
1835
1836                 if( !noFilterOnPurpose && (propFilterHash == null || propFilterHash.isEmpty()) ){
1837                         // They should have passed at least one property to filter on
1838                         throw new AAIException("AAI_6118", "Required field: propFilterHash not passed to getNodes()."); 
1839                 }
1840
1841                 ArrayList<String> kName = new ArrayList<String>();
1842                 ArrayList<Object> kVal = new ArrayList<Object>();
1843                 int i = -1;
1844                 Collection <String> indexedProps =  dbMaps.NodeMapIndexedProps.get(nodeType); 
1845                 // First loop through to pick up the indexed-properties if there are any being used
1846
1847                 if( propFilterHash != null ){
1848                         Iterator <?> it = propFilterHash.entrySet().iterator();
1849                         while( it.hasNext() ){
1850                                 Map.Entry<?,?> propEntry = (Map.Entry<?,?>) it.next();
1851                                 String propName = (propEntry.getKey()).toString();
1852                                 // Don't allow search on properties that do not have SINGLE cardinality
1853                                 if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List")  ){
1854                                         if( indexedProps.contains(propName) ){
1855                                                 i++;
1856                                                 kName.add(i, propName);
1857                                                 kVal.add(i, (Object)propEntry.getValue());
1858                                         }
1859                                 }
1860                         }
1861
1862                         // Now go through again and pick up the non-indexed properties
1863                         it = propFilterHash.entrySet().iterator();
1864                         while( it.hasNext() ){
1865                                 Map.Entry <?,?> propEntry = (Map.Entry<?,?>)it.next();
1866                                 String propName = (propEntry.getKey()).toString();
1867                                 // Don't allow search on properties that do not have SINGLE cardinality
1868                                 if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List")  ){
1869                                         if( ! indexedProps.contains(propName) ){
1870                                                 i++;
1871                                                 kName.add(i, propName);
1872                                                 kVal.add(i, (Object)propEntry.getValue());
1873                                         }
1874                                 }
1875                         }
1876                 }
1877
1878                 Iterable <?> verts = null;
1879                 String propsAndValuesForMsg = "";
1880                 int topPropIndex = i;
1881                 if( topPropIndex == -1 ){
1882                         // No Filtering -- just go get them all
1883                         verts= graph.query().has("aai-node-type",nodeType).vertices();
1884                         propsAndValuesForMsg = " ( no filter props ) ";
1885                 }       
1886                 else if( topPropIndex == 0 ){
1887                         verts= graph.query().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType).vertices();
1888                         propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") ";
1889                 }       
1890                 else if( topPropIndex == 1 ){
1891                         verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has("aai-node-type",nodeType).vertices();
1892                         propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1893                                         + kName.get(1) + " = " + kVal.get(1) + ") ";
1894                 }                       
1895                 else if( topPropIndex == 2 ){
1896                         verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has("aai-node-type",nodeType).vertices();
1897                         propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1898                                         + kName.get(1) + " = " + kVal.get(1) + ", " 
1899                                         + kName.get(2) + " = " + kVal.get(2) +  ") ";
1900                 }       
1901                 else if( topPropIndex == 3 ){
1902                         verts= graph.query().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has(kName.get(2),kVal.get(2)).has(kName.get(3),kVal.get(3)).has("aai-node-type",nodeType).vertices();
1903                         propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
1904                                         + kName.get(1) + " = " + kVal.get(1) + ", " 
1905                                         + kName.get(2) + " = " + kVal.get(2) + ", " 
1906                                         + kName.get(3) + " = " + kVal.get(3) +  ") ";
1907                 }                       
1908                 else {
1909                         String emsg = " -- Sorry -- we only support 4 filter properties in getNodes() for now... \n";
1910                         throw new AAIException("AAI_6114", emsg); 
1911                 }   
1912                 if( verts != null ){
1913                         // We did find some matching vertices
1914                         Iterator <?> it = verts.iterator();
1915                         while( it.hasNext() ){
1916                                 TitanVertex v = (TitanVertex)it.next();
1917                                 
1918                                 if( skipGroomCheck ){
1919                                         // Good or bad, just return everything we find
1920                                         returnVertList.add( v );
1921                                 }
1922                                 else {
1923                                         // Weed out any bad vertices we find
1924                                         if( thisVertexNotReachable(transId, fromAppId, graph, v, apiVersion) ){
1925                                                 LOGGER.info("IN-LINE GROOMING - Unreachable Node DETECTED > skipping it. ");
1926                                         }
1927                                         else if( thisVertexHasBadEdges(transId, fromAppId, graph, v, apiVersion) ){
1928                                                 LOGGER.info("IN-LINE GROOMING - BAD EDGE DETECTED > skipping vtxId = [" + v.id() + "] ");
1929                                         }
1930                                         else if( thisVertexIsAPhantom(transId, fromAppId, graph, v, apiVersion) ){
1931                                                 LOGGER.info("IN-LINE GROOMING - BAD NODE DETECTED > skipping vtxId = [" + v.id() + "] ");
1932                                         }
1933                                         else {
1934                                                 returnVertList.add( v );
1935                                         }
1936                                 }
1937                         }
1938                 }
1939                 
1940                 return returnVertList;
1941         }
1942         
1943         /**
1944          * Gets the nodes.
1945          *
1946          * @param transId the trans id
1947          * @param fromAppId the from app id
1948          * @param graph the graph
1949          * @param nodeType the node type
1950          * @param propFilterHash the prop filter hash
1951          * @param noFilterOnPurpose the no filter on purpose
1952          * @return the nodes
1953          * @throws AAIException the AAI exception
1954          */
1955         @Deprecated
1956         public static ArrayList<TitanVertex> getNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
1957                         HashMap<String,Object> propFilterHash, Boolean noFilterOnPurpose ) throws AAIException{
1958                 return getNodes(transId,  fromAppId,  graph,  nodeType,
1959                                 propFilterHash,  noFilterOnPurpose,  null );
1960         }
1961         // End of getNodes()
1962
1963
1964         /**
1965          * Gets the connected children.
1966          *
1967          * @param transId the trans id
1968          * @param fromAppId the from app id
1969          * @param graph the graph
1970          * @param startVtx the start vtx
1971          * @param limitToThisNodeType the limit to this node type
1972          * @return ArrayList <TitanVertex>
1973          * @throws AAIException the AAI exception
1974          */
1975         public static ArrayList<TitanVertex> getConnectedChildren( String transId, String fromAppId, TitanTransaction graph, 
1976                         TitanVertex startVtx, String limitToThisNodeType ) throws AAIException{
1977                 
1978                 // Just get child nodes (ie. other end of an OUT edge that is tagged as a parent/Child edge)
1979                  
1980                 ArrayList <TitanVertex> childList = new ArrayList <TitanVertex> ();
1981                 Boolean doNodeTypeCheck = false;
1982                 if( limitToThisNodeType != null && ! limitToThisNodeType.equals("") ){
1983                         doNodeTypeCheck = true;
1984                 }
1985                 
1986                 
1987                 List<Vertex> verts = graph.traversal().V(startVtx).union(__.inE().has("isParent-REV", true).outV(), __.outE().has("isParent", true).inV()).toList();
1988                 TitanVertex tmpVtx = null;
1989                 int vertsSize = verts.size();
1990                 for (int i = 0; i < vertsSize; i++){
1991                         tmpVtx = (TitanVertex) verts.get(i);
1992                         if( ! doNodeTypeCheck ){ 
1993                                 childList.add(tmpVtx);
1994                         }
1995                         else {
1996                                 String tmpNT = tmpVtx.<String>property("aai-node-type").orElse(null);
1997                                 if( tmpNT != null && tmpNT.equals(limitToThisNodeType) ){
1998                                         childList.add(tmpVtx);
1999                                 }
2000                         }
2001                 }
2002                 
2003                 return childList;               
2004
2005         }// End of getConnectedChildren()
2006
2007
2008
2009         /**
2010          * Gets the connected nodes.
2011          *
2012          * @param transId the trans id
2013          * @param fromAppId the from app id
2014          * @param graph the graph
2015          * @param nodeType the node type
2016          * @param propFilterHash the prop filter hash
2017          * @param startNodeVal the start node val
2018          * @param apiVersion the api version
2019          * @param excludeRecurComingIn the exclude recur coming in
2020          * @return ArrayList <TitanVertex>
2021          * @throws AAIException the AAI exception
2022          */
2023         public static ArrayList<TitanVertex> getConnectedNodes( String transId, String fromAppId, TitanTransaction graph, String nodeType,
2024                         HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, String apiVersion, Boolean excludeRecurComingIn ) throws AAIException{
2025                 
2026                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
2027                 /* Get (almost) all the nodes that are connected to this vertex.  
2028                  * Narrow down what is returned using optional filter parameters nodeType and propFilterHash
2029                  * NOTE - the default behavior has changed slightly.  For start-Nodes that
2030                  *        can be recursivly connected, this method will only bring back the same kind of
2031                  *        connected node by following an OUT edge.   Ie. if the start node is an "model-element", 
2032                  *        then this method will only follow OUT edges to get to other "model-element" type nodes.
2033                  */
2034
2035                 String startNodeNT = "";
2036                 if( startNodeVal == null ){
2037                         // They should have passed in the node that this query starts from
2038                         throw new AAIException("AAI_6109", "null startNode object passed to getConnectedNodes()."); 
2039                 }       
2040                 else {
2041                         startNodeNT = startNodeVal.<String>property("aai-node-type").orElse(null);
2042                 }
2043                 
2044                 boolean nodeTypeFilter = false;
2045                 if( nodeType != null && !nodeType.equals("") ){
2046                         // They want to filter using nodeType
2047                         if( ! dbMaps.NodeProps.containsKey(nodeType) ){
2048                                 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to getConnectedNodes()."); 
2049                         }
2050                         nodeTypeFilter = true;
2051                 }  
2052                 
2053                 ArrayList <String> excludeVidList = new <String> ArrayList ();
2054                 if( DbEdgeRules.CanBeRecursiveNT.containsKey(startNodeNT) && excludeRecurComingIn ){
2055                         // If we're starting on a nodeType that supports recursion, then find any connected
2056                         // nodes that are coming from IN edges so we can exclude them later.
2057                         
2058                         Iterable <?> vertsR = startNodeVal.query().direction(Direction.IN).vertices();
2059                         Iterator <?> vertIR = vertsR.iterator();
2060                         while( vertIR != null && vertIR.hasNext() ){
2061                                 TitanVertex tmpVertIN = (TitanVertex) vertIR.next();
2062                                 String tmpNT = tmpVertIN.<String>property("aai-node-type").orElse(null);
2063                                 if( tmpNT != null && tmpNT.equals(startNodeNT) ){
2064                                         // We're on a nodetype that supports recursion (like model-element) and we've
2065                                         // found an connected Node of this same type on an IN edge - put this 
2066                                         // on our excludeList.
2067                                         excludeVidList.add( tmpVertIN.id().toString() );
2068                                 }
2069                         }
2070                 }
2071         
2072                 boolean propertyFilter = false;
2073                 if( propFilterHash != null && !propFilterHash.isEmpty() ){
2074                         // They want to filter using some properties
2075                         Iterator <?> it = propFilterHash.entrySet().iterator();
2076                         while( it.hasNext() ){
2077                                 Map.Entry<?,?> propEntry = (Map.Entry<?,?>)it.next();
2078                                 String propName = (propEntry.getKey()).toString();
2079                                 if( ! dbMaps.NodeProps.containsValue(propName) ){
2080                                         throw new AAIException("AAI_6116", "Unrecognized property name [" + propName + "] passed to getConnectedNodes()."); 
2081                                 }
2082                                 // Don't allow search on properties that do not have SINGLE cardinality
2083                                 if( !checkPropCardinality(propName, "Set") && !checkPropCardinality(propName, "List")  ){
2084                                         propertyFilter = true;
2085                                 }
2086                         }
2087                 }
2088                 // If filter-properties were passed in, then look for nodes that have those values.
2089                 ArrayList<TitanVertex> returnVertList = new ArrayList<TitanVertex>();
2090                 Iterable<TitanVertex> qResult = null;
2091                 Iterator<TitanVertex> resultI = null;
2092                 try {
2093                         qResult = startNodeVal.query().vertices();
2094                         resultI = qResult.iterator();
2095                 }
2096                 catch( NullPointerException npe ){
2097                         throw new AAIException("AAI_6125", "Titan null pointer exception trying to get nodes connected to vertexId = " + 
2098                                         startNodeVal.id() + ", aai-node-type = [" + startNodeVal.property("aai-node-type") + "]."); 
2099                 }
2100
2101                 while( resultI != null && resultI.hasNext() ){
2102                         boolean addThisOne = true;
2103                         TitanVertex tmpV = (TitanVertex)resultI.next();                 
2104                         if( tmpV == null ){
2105                                 LOGGER.info("Titan gave a null vertex when looking for nodes connected to vertexId = " + 
2106                                                 startNodeVal.id() + ", aai-node-type = [" + startNodeVal.property("aai-node-type") + "].");
2107                                 // Note - we will skip this one, but try to return any others that we find.
2108                                 addThisOne = false;
2109                         }
2110
2111                 else {
2112                                 String tmpVid = tmpV.id().toString();
2113                                 if( nodeTypeFilter ){
2114                                         Object nto = tmpV.<Object>property("aai-node-type").orElse(null);
2115                                         if( nto == null || !nto.toString().equals(nodeType) ){ 
2116                                                 //LOGGER.info("Found a connected vertex (vertexId = " + 
2117                                                 //              tmpVid + "), but we will not collect it.  It had aai-node-type [" +
2118                                                 //              nto + "], we are looking for [" + nodeType + "]. ");
2119                                                 // Note - we will skip this one, but try to return any others that we find.
2120                                                 addThisOne = false;
2121                                         }
2122                                 }
2123                                 
2124                                 if( excludeVidList.contains(tmpVid) ){
2125                                         LOGGER.info("Found a connected vertex (vertexId = " + 
2126                                                         tmpVid + "), but will exclude it since it is on an IN edge and this nodeType " +
2127                                                         startNodeNT + " can be recursively attached.");
2128                                         // Note - we will skip this one, but try to return any others that we find.
2129                                         addThisOne = false;
2130                                 }
2131                         
2132                                 if( propertyFilter ){
2133                                         Iterator <?> it = propFilterHash.entrySet().iterator();
2134                                         while( it.hasNext() ){
2135                                                 Map.Entry <?,?>propEntry = (Map.Entry<?,?>)it.next();
2136                                                 String propName = (propEntry.getKey()).toString();
2137                                                 if( checkPropCardinality(propName, "Set") || checkPropCardinality(propName, "List")  ){
2138                                                         // Don't allow search on properties that do not have SINGLE cardinality
2139                                                         continue;
2140                                                 }
2141                                                 Object propVal =  propEntry.getValue();
2142                                                 Object foundVal = tmpV.<Object>property(propName).orElse(null);
2143                                                 if( foundVal != null && propVal != null && !foundVal.toString().equals(propVal.toString()) ){
2144                                                         addThisOne = false;
2145                                                         break;
2146                                                 }
2147                                                 else if( (foundVal == null && propVal != null) || (foundVal != null && propVal == null) ){
2148                                                         addThisOne = false;
2149                                                         break;
2150                                                 }
2151                                         }
2152                                 }
2153                         }
2154                         if( addThisOne ){
2155                                 // This node passed the tests -- put it on the return List
2156                                 returnVertList.add( (TitanVertex)tmpV );
2157                         }
2158                 }
2159                 //aaiLogger.debug(logline, " end ");
2160                 return returnVertList;
2161
2162         }// End of getConnectedNodes()
2163
2164
2165         /**
2166          * Gets the connected nodes.
2167          *
2168          * @param transId the trans id
2169          * @param fromAppId the from app id
2170          * @param graph the graph
2171          * @param nodeType the node type
2172          * @param propFilterHash the prop filter hash
2173          * @param startNodeVal the start node val
2174          * @param apiVersion the api version
2175          * @return the connected nodes
2176          * @throws AAIException the AAI exception
2177          */
2178         @Deprecated
2179         public static ArrayList<TitanVertex> getConnectedNodes(String transId, String fromAppId, TitanTransaction graph, String nodeType,
2180                         HashMap<String,Object> propFilterHash, TitanVertex startNodeVal, String apiVersion ) throws AAIException {
2181                 return getConnectedNodes( transId,  fromAppId,  graph,  nodeType,
2182                                 propFilterHash,  startNodeVal,  apiVersion, true );
2183         }
2184         
2185         /**
2186          * Gets the connected nodes.
2187          *
2188          * @param transId the trans id
2189          * @param fromAppId the from app id
2190          * @param graph the graph
2191          * @param nodeType the node type
2192          * @param propFilterHash the prop filter hash
2193          * @param startNodeVal the start node val
2194          * @return the connected nodes
2195          * @throws AAIException the AAI exception
2196          */
2197         @Deprecated
2198         public static ArrayList<TitanVertex> getConnectedNodes(String transId, String fromAppId, TitanTransaction graph, String nodeType,
2199                         HashMap<String,Object> propFilterHash, TitanVertex startNodeVal ) throws AAIException {
2200                 return getConnectedNodes( transId,  fromAppId,  graph,  nodeType,
2201                                 propFilterHash,  startNodeVal,  null, true );
2202
2203         }
2204         
2205         /**
2206          * Ip address format OK.
2207          *
2208          * @param transId the trans id
2209          * @param fromAppId the from app id
2210          * @param addrVal the addr val
2211          * @param addrVer the addr ver
2212          * @param apiVersion the api version
2213          * @return Boolean
2214          * @throws AAIException the AAI exception
2215          */
2216         public static Boolean ipAddressFormatOK(String transId, String fromAppId, String addrVal, String addrVer, String apiVersion) throws AAIException{
2217
2218                 /* NOTE -- the google methods we use do not allow leading zeros in ipV4 addresses. 
2219                  *     So it will reject, "22.33.44.001"
2220                  */
2221
2222                 if( addrVal == null ){
2223                         throw new AAIException("AAI_6120", "Bad data (addrVal = null) passed to ipAddressFormatOK()"); 
2224                 }
2225                 else if( addrVer == null ){
2226                         throw new AAIException("AAI_6120", "Bad data (addrType = null) passed to ipAddressFormatOK()"); 
2227                 }
2228
2229                 Boolean retVal = false;
2230                 Boolean lookingForV4 = false;
2231                 Boolean lookingForV6 = false;
2232                 InetAddress inetAddr = null;
2233
2234                 if( addrVer.equalsIgnoreCase("v4") || addrVer.equals("ipv4") || addrVer.equals("4")){
2235                         lookingForV4 = true;
2236                 }
2237                 else if( addrVer.equalsIgnoreCase("v6") || addrVer.equals("ipv6") || addrVer.equals("6")){
2238                         lookingForV6 = true;
2239                 }
2240                 else {
2241                         throw new AAIException("AAI_6120", " Bad data for addressVersion [" + addrVer + "] passed to ipAddressFormatOK()"); 
2242                 }
2243
2244                 try {
2245                         inetAddr = InetAddresses.forString(addrVal);
2246                         if( inetAddr instanceof Inet4Address ){
2247                                 if( lookingForV4 ){
2248                                         retVal = true;
2249                                 }
2250                                 else {
2251                                         throw new AAIException("AAI_6120", "Bad data. Address is a V4, but addressType said it should be V6.  [" 
2252                                                         + addrVal + "], [" + addrVer + "] passed to ipAddressFormatOK()"); 
2253                                 }
2254                         }
2255                         else if( inetAddr instanceof Inet6Address ){
2256                                 if( lookingForV6 ){
2257                                         retVal = true;
2258                                 }
2259                                 else {
2260                                         throw new AAIException("AAI_6120", "Bad data. Address is a V6, but addressType said it should be V4.  [" 
2261                                                         + addrVal + "], [" + addrVer + "] passed to ipAddressFormatOK()."); 
2262                                 }
2263                         }        
2264                 } 
2265                 catch (IllegalArgumentException e) {
2266                         throw new AAIException("AAI_6120", "Badly formed ip-address:  [" + addrVal + "] passed to ipAddressFormatOK()"); 
2267                 }
2268
2269                 return retVal;
2270
2271         }//end of ipAddressFormatOk()
2272         
2273         /**
2274          * Ip address format OK.
2275          *
2276          * @param transId the trans id
2277          * @param fromAppId the from app id
2278          * @param addrVal the addr val
2279          * @param addrVer the addr ver
2280          * @return the boolean
2281          * @throws AAIException the AAI exception
2282          */
2283         public static Boolean ipAddressFormatOK(String transId, String fromAppId, String addrVal, String addrVer) throws AAIException{
2284                 return ipAddressFormatOK( transId,  fromAppId,  addrVal,  addrVer, null);
2285         }
2286         
2287         /**
2288          * Save aai edge to db.
2289          *
2290          * @param transId the trans id
2291          * @param fromAppId the from app id
2292          * @param graph the graph
2293          * @param edgeLabel the edge label
2294          * @param outV the out V
2295          * @param inV the in V
2296          * @param propHash the prop hash
2297          * @param apiVersion the api version
2298          * @return TitanEdge
2299          * @throws AAIException the AAI exception
2300          */
2301         private static TitanEdge saveAaiEdgeToDb(String transId, String fromAppId, TitanTransaction graph, String edgeLabel, 
2302                         TitanVertex outV, TitanVertex inV, HashMap <String,Object> propHash, String apiVersion) throws AAIException{
2303
2304                 // If this edge doesn't exist yet, then create it.
2305
2306                 // NOTE - the Titan javaDoc says that there might not always be an id for a node.
2307                 //   This is the internal-titan-unique-id, not any of our data.
2308                 //   Not sure how to know when it might be there and when it might not?!
2309                 //   So far, it has worked for all my testing, but this might warrant some
2310                 //   further investigation.
2311
2312                 TitanEdge existingEdge = null;
2313                 String inVId =  inV.id().toString();
2314                 Iterator <Edge> eI = outV.edges(Direction.BOTH, edgeLabel);
2315                 while( eI.hasNext() ){
2316                         TitanEdge ed = (TitanEdge) eI.next();
2317                         TitanVertex otherVtx = (TitanVertex) ed.otherVertex(outV);
2318                         if( (otherVtx.id().toString()).equals(inVId) ){
2319                                 // NOTE -?- Not sure -- at some point we might want to check the edgeLabels also since  we might
2320                                 //   want to allow two different-type edges between the same two vertexes?  (or maybe not.)
2321                                 existingEdge = ed;
2322                                 break;
2323                         }
2324                 }
2325
2326                 if( existingEdge != null ){
2327                         // This is just an UPDATE
2328                         for( Map.Entry<String, Object> entry : propHash.entrySet() ){
2329                                 LOGGER.debug("update edge property/val = [" + entry.getKey() + "]/[" + entry.getValue() + "]");
2330                                 existingEdge.property( entry.getKey(), entry.getValue() );
2331                         }
2332
2333                         return( existingEdge );
2334                 }
2335                 else {
2336                         // This is an ADD
2337                         
2338                         // Uniqueness double-check.   This is just to catch the possibility that at the transaction layer,
2339                         //    if data came in for two identical nodes that point to the same dependant node (for uniqueness), 
2340                         //    we would only be able to catch the problem at the time the edge to the second node is added.   
2341                         //    For example - if they had a VM and then got a request to add two ipAddress nodes, but some 
2342                         //    bad data was passed and those two ipAddress nodes were identical -- we'd want to catch it.
2343                         String outV_NType = outV.<String>property("aai-node-type").orElse(null);
2344                         String inV_NType = inV.<String>property("aai-node-type").orElse(null);
2345                         if( needsADepNode4Uniqueness(transId, fromAppId, outV_NType, apiVersion)  
2346                                         &&  nodeTypeACanDependOnB(transId, fromAppId, outV_NType, inV_NType, apiVersion) ){
2347                                 // The out-Vertex has a uniqueness dependency on the in-vertex
2348                                 // Make sure we haven't already added an node/edge like this in this transaction
2349                                 HashMap <String, Object> nodeKeyPropsHash = getNodeKeyPropHash(transId, fromAppId, graph, outV); 
2350                                 ArrayList<TitanVertex> resultList = new ArrayList<TitanVertex>();
2351                                 resultList = DbMeth.getConnectedNodes("transId", "fromAppId", graph, outV_NType, nodeKeyPropsHash, inV, apiVersion, false);
2352                                 if( resultList.size() > 0 ){
2353                                         String propInfo = "";
2354                                         if( nodeKeyPropsHash != null ){
2355                                                 propInfo = nodeKeyPropsHash.toString();
2356                                         }
2357                                         throw new AAIException("AAI_6117", "Failed to add edge.  This node (" + inV_NType + ") already has an edge to a " + outV_NType + 
2358                                                         " node with kepProps [" + propInfo + "]");  
2359                                 }
2360                         }
2361                         else if( needsADepNode4Uniqueness(transId, fromAppId, inV_NType, apiVersion)  
2362                                         &&  nodeTypeACanDependOnB(transId, fromAppId, inV_NType, outV_NType, apiVersion) ){     
2363                                 // The in-Vertex has a uniqueness dependency on the out-vertex
2364                                 // Make sure we haven't already added an node/edge like this in this transaction
2365                                 HashMap <String, Object> nodeKeyPropsHash = getNodeKeyPropHash(transId, fromAppId, graph, inV);
2366                                 ArrayList<TitanVertex> resultList = new ArrayList<TitanVertex>();
2367                                 resultList = DbMeth.getConnectedNodes("transId", "fromAppId", graph, inV_NType, nodeKeyPropsHash, outV, apiVersion, false);
2368                                 if( resultList.size() > 0 ){
2369                                         String propInfo = "";
2370                                         if( nodeKeyPropsHash != null ){
2371                                                 propInfo = nodeKeyPropsHash.toString();
2372                                         }
2373                                         throw new AAIException("AAI_6117", "Failed to add edge.  This node (" + outV_NType + ") already has an edge to a " + inV_NType + 
2374                                                         " node with kepProps [" + propInfo + "]");  
2375                                 }
2376                         }
2377                         
2378                         
2379                         // We're good to go to add this edge
2380
2381                         TitanEdge tEdge =  outV.addEdge( edgeLabel, inV );
2382                         // Add the properties to the new Edge
2383                         for( Map.Entry<String, Object> entry : propHash.entrySet() ){
2384                                 tEdge.property( entry.getKey(), entry.getValue() );
2385                         }
2386                         
2387                         // For (resource-id updates) we need to "touch" the vertices on each side of the edge so
2388                         // anybody working on one of those vertices will know that something (ADDing this edge) has happened.
2389                         touchVertex( transId, fromAppId, inV );
2390                         touchVertex( transId, fromAppId, outV );
2391                         
2392                         return tEdge;
2393                 }
2394
2395         }// End saveAaiEdgeToDb()
2396
2397         
2398         
2399         /**
2400          * Derive edge rule key for this edge.
2401          *
2402          * @param transId the trans id
2403          * @param fromAppId the from app id
2404          * @param graph the graph
2405          * @param tEdge the t edge
2406          * @return String - key to look up edgeRule (fromNodeType|toNodeType)
2407          * @throws AAIException the AAI exception
2408          */
2409         public static String deriveEdgeRuleKeyForThisEdge( String transId, String fromAppId, TitanTransaction graph,  
2410                         TitanEdge tEdge ) throws AAIException{
2411
2412                 TitanVertex fromVtx = tEdge.outVertex();
2413                 TitanVertex toVtx = tEdge.inVertex();
2414                 String startNodeType = fromVtx.<String>property("aai-node-type").orElse(null);
2415                 String targetNodeType = toVtx.<String>property("aai-node-type").orElse(null);
2416                 String key = startNodeType + "|" + targetNodeType;
2417                 if( EdgeRules.getInstance().hasEdgeRule(startNodeType, targetNodeType) ){
2418                         // We can use the node info in the order they were given
2419                         return( key );
2420                 }
2421                 else {
2422                         key = targetNodeType + "|" + startNodeType;
2423                         if( EdgeRules.getInstance().hasEdgeRule(targetNodeType, startNodeType) ){
2424                                 return( key );
2425                         }
2426                         else {
2427                                 // Couldn't find a rule for this edge
2428                                 throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + startNodeType + ", " 
2429                                                 + targetNodeType); 
2430                         }
2431                 }
2432         }// end of deriveEdgeRuleKeyForThisEdge()
2433         
2434         
2435
2436         /**
2437          * Save aai edge to db.
2438          *
2439          * @param transId the trans id
2440          * @param fromAppId the from app id
2441          * @param graph the graph
2442          * @param edgeLabel the edge label
2443          * @param outV the out V
2444          * @param inV the in V
2445          * @param propHash the prop hash
2446          * @return the titan edge
2447          * @throws AAIException the AAI exception
2448          */
2449         @Deprecated
2450         private static TitanEdge saveAaiEdgeToDb(String transId, String fromAppId, TitanTransaction graph, String edgeLabel, 
2451                         TitanVertex outV, TitanVertex inV, HashMap <String,Object> propHash) throws AAIException{
2452                 return  saveAaiEdgeToDb( transId,  fromAppId,  graph,  edgeLabel, 
2453                                 outV,  inV, propHash, null);
2454         }
2455         
2456         /**
2457          * Persist aai edge.
2458          *
2459          * @param transId the trans id
2460          * @param fromAppId the from app id
2461          * @param graph the graph
2462          * @param startVert the start vert
2463          * @param targetVert the target vert
2464          * @param apiVersion the api version
2465          * @return the titan edge
2466          * @throws AAIException the AAI exception
2467          */
2468         public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph,  
2469                         TitanVertex startVert, TitanVertex targetVert, String apiVersion ) throws AAIException{
2470                 TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion, "");
2471                 return returnEdge;
2472         }
2473         
2474         /**
2475          * Persist aai edge.
2476          *
2477          * @param transId the trans id
2478          * @param fromAppId the from app id
2479          * @param graph the graph
2480          * @param startVert the start vert
2481          * @param targetVert the target vert
2482          * @param apiVersion the api version
2483          * @param edgeType the edge type
2484          * @return TitanEdge
2485          * @throws AAIException the AAI exception
2486          */
2487         public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph,  
2488                         TitanVertex startVert, TitanVertex targetVert, String apiVersion, String edgeType ) throws AAIException{
2489
2490                 TitanVertex fromVtx = null;
2491                 TitanVertex toVtx = null;
2492                 String startNodeType = startVert.<String>property("aai-node-type").orElse(null);
2493                 String targetNodeType = targetVert.<String>property("aai-node-type").orElse(null);
2494                 String fwdRuleKey = startNodeType + "|" + targetNodeType;
2495                 int fwdRuleCount = 0;
2496                 String fwdRule = "";
2497                 String fwdLabel = "";
2498                 String revRuleKey = targetNodeType + "|" + startNodeType;
2499                 int revRuleCount = 0;   
2500                 String revRule = "";
2501                 String revLabel = "";
2502                 String edRule = "";
2503                 String edLabel = "";
2504                 
2505                 Boolean checkType = false;
2506                 if( (edgeType != null) && edgeType != "" ){
2507                         checkType = true;
2508                 }
2509                 
2510                 // As of 16-07, it is possible to have more than one kind of edge defined between a given 
2511                 // pair of nodeTypes.   So we need to check to see if there is only one possibility, or if
2512                 // we need to look at the edgeType to determine which to use.  
2513                 // NOTE -- we're only supporting having 2 edges between a given pair of nodeTypes and
2514                 //    one and only one of them would have to be a parent-child edge.
2515                 
2516                 if( DbEdgeRules.EdgeRules.containsKey(fwdRuleKey) ){
2517                         Collection <String> edRuleColl = DbEdgeRules.EdgeRules.get(fwdRuleKey);
2518                         Iterator <String> ruleItr = edRuleColl.iterator();
2519                         while( ruleItr.hasNext() ){
2520                                 String tmpRule = ruleItr.next();
2521                                 String [] rules = tmpRule.split(",");
2522                                 String tmpLabel = rules[0];
2523                                 String tmpParChild = rules[3];
2524                                 if( !checkType 
2525                                                 || (checkType && tmpParChild.equals("true") && edgeType.equals("parentChild"))
2526                                                 || (checkType && tmpParChild.equals("false") && edgeType.equals("cousin"))   ){
2527                                         // Either they didn't want us to check the edgeType or it is a match
2528                                         fwdRuleCount++;
2529                                         if( fwdRuleCount > 1 ){
2530                                                 // We found more than one with the given info
2531                                                 throw new AAIException("AAI_6120", "Multiple EdgeRules found for nodeTypes: [" + startNodeType + "], [" 
2532                                                                 + targetNodeType + "], edgeType = [" + edgeType + "]."); 
2533                                         }
2534                                         else {
2535                                                 fwdRule = tmpRule;
2536                                                 fwdLabel = tmpLabel;
2537                                         }
2538                                 }
2539                         }
2540                 }
2541                 
2542                 // Try it the other way also (unless this is the case of a nodeType recursively pointing to itself 
2543                 // Ie. the edge rule:  "model-element|model-element"
2544                 if( !revRuleKey.equals(fwdRuleKey) && DbEdgeRules.EdgeRules.containsKey(revRuleKey) ){
2545                         Collection <String> edRuleColl = DbEdgeRules.EdgeRules.get(revRuleKey);
2546                         Iterator <String> ruleItr = edRuleColl.iterator();
2547                         while( ruleItr.hasNext() ){
2548                                 String tmpRule = ruleItr.next();
2549                                 String [] rules = tmpRule.split(",");
2550                                 String tmpLabel = rules[0];
2551                                 String tmpParChild = rules[3];
2552                                 if( !checkType 
2553                                                 || (checkType && tmpParChild.equals("true") && edgeType.equals("parentChild"))
2554                                                 || (checkType && tmpParChild.equals("false") && edgeType.equals("cousin"))   ){
2555                                         // Either they didn't want us to check the edgeType or it is a match
2556                                         revRuleCount++;
2557                                         if( revRuleCount > 1 ){
2558                                                 // We found more than one with the given info
2559                                                 throw new AAIException("AAI_6120", "Multiple EdgeRules found for nodeTypes: [" + targetNodeType + "], [" 
2560                                                                 + startNodeType + "], edgeType = [" + edgeType + "]."); 
2561                                         }
2562                                         else {
2563                                                 revRule = tmpRule;
2564                                                 revLabel = tmpLabel;
2565                                         }
2566                                 }
2567                         }
2568                 }
2569                         
2570                 if( (fwdRuleCount == 1) && (revRuleCount == 0) ){
2571                         // We can use the node info in the order they were given
2572                         fromVtx = startVert;
2573                         toVtx = targetVert;
2574                         edRule = fwdRule;
2575                         edLabel = fwdLabel;
2576                 }
2577                 else if( (fwdRuleCount == 0) && (revRuleCount == 1) ){
2578                         // We need to switch the vertex order so the edge-direction is correct
2579                         toVtx = startVert;
2580                         fromVtx = targetVert;
2581                         edRule = revRule;
2582                         edLabel = revLabel;
2583                 }
2584                 else if( (fwdRuleCount == 0) && (revRuleCount == 0) ){
2585                         // No edge rule found for this
2586                         throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + startNodeType + ", " + targetNodeType 
2587                                         + "], checkLabelType = [" + edgeType + "]."); 
2588                 }       
2589                 else if( (fwdRuleCount > 0) && (revRuleCount > 0) ){
2590                         // We found more than one with the given info
2591                         throw new AAIException("AAI_6120", "Multiple EdgeRules (fwd and rev) found for nodeTypes: [" + startNodeType + "], [" 
2592                                         + targetNodeType + "], checkLabelType = [" + edgeType + "]."); 
2593                 }
2594                 
2595                 // If we got to this point, we now have a single edge label and we know to and from Vtx.
2596                 
2597                 HashMap <String,Object> edgeParamHash = getEdgeTagPropPutHash4Rule(transId, fromAppId, edRule);
2598                 // We do "source-of-truth" for all edges
2599                 edgeParamHash.put("source-of-truth", fromAppId );
2600
2601                 TitanEdge returnEdge = saveAaiEdgeToDb(transId, fromAppId, graph, edLabel, fromVtx, toVtx, edgeParamHash, apiVersion);
2602
2603                 return returnEdge;
2604
2605         }
2606         
2607         /**
2608          * Persist aai edge.
2609          *
2610          * @param transId the trans id
2611          * @param fromAppId the from app id
2612          * @param graph the graph
2613          * @param startVert the start vert
2614          * @param targetVert the target vert
2615          * @return the titan edge
2616          * @throws AAIException the AAI exception
2617          */
2618         @Deprecated
2619         public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph,  
2620                         TitanVertex startVert, TitanVertex targetVert ) throws AAIException{
2621                 return persistAaiEdge( transId,  fromAppId,  graph,  
2622                                 startVert,  targetVert, null);
2623         }
2624         // End persistAaiEdge()
2625
2626
2627         /**
2628          * Persist aai edge.
2629          *
2630          * @param transId the trans id
2631          * @param fromAppId the from app id
2632          * @param graph the graph
2633          * @param edgeLabel the edge label
2634          * @param startVert the start vert
2635          * @param targetVert the target vert
2636          * @param propHash the prop hash
2637          * @param addIfNotFound the add if not found
2638          * @return the titan edge
2639          * @throws AAIException the AAI exception
2640          */
2641         @Deprecated
2642         public static TitanEdge persistAaiEdge( String transId, String fromAppId, TitanTransaction graph,  
2643                         String edgeLabel, TitanVertex startVert, TitanVertex targetVert, 
2644                         HashMap <String,Object> propHash, Boolean addIfNotFound ) throws AAIException{  
2645
2646                 /*----- This method is depricated ------
2647                  *  We will ignore the parameters: edgeLabel, propHash and addIfNotFound
2648                  *  We will use the remaining params to call the newer version of this method
2649                  */
2650                 TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, null);
2651
2652                 return returnEdge;
2653
2654         }// End depricated version of persistAaiEdge()
2655
2656
2657         /**
2658          * Persist aai edge with dep params.
2659          *
2660          * @param transId the trans id
2661          * @param fromAppId the from app id
2662          * @param graph the graph
2663          * @param startVert the start vert
2664          * @param targetNodeType the target node type
2665          * @param targetNodeParamHash the target node param hash
2666          * @param apiVersion the api version
2667          * @return TitanEdge
2668          * @throws AAIException the AAI exception
2669          */
2670         public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph,  
2671                         TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash, String apiVersion) throws AAIException{
2672
2673                 TitanVertex targetVert = getUniqueNodeWithDepParams( transId, fromAppId, graph, targetNodeType, targetNodeParamHash, apiVersion );
2674                 TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion);
2675
2676                 return returnEdge;
2677
2678         }// End persistAaiEdgeWithDepParams()
2679         
2680         // Version that lets you pass in an edgeType ("parentChild" or "cousin" since it sometimes cannot be determined 
2681         /**
2682          * Persist aai edge with dep params.
2683          *
2684          * @param transId the trans id
2685          * @param fromAppId the from app id
2686          * @param graph the graph
2687          * @param startVert the start vert
2688          * @param targetNodeType the target node type
2689          * @param targetNodeParamHash the target node param hash
2690          * @param apiVersion the api version
2691          * @param edgeType the edge type
2692          * @return the titan edge
2693          * @throws AAIException the AAI exception
2694          */
2695         // from the two nodeTypes anymore (16-07)
2696         public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph,  
2697                         TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash, 
2698                         String apiVersion, String edgeType) throws AAIException{
2699                 TitanVertex targetVert = getUniqueNodeWithDepParams( transId, fromAppId, graph, targetNodeType, targetNodeParamHash, apiVersion );
2700                 TitanEdge returnEdge = persistAaiEdge(transId, fromAppId, graph, startVert, targetVert, apiVersion, edgeType);
2701
2702                 return returnEdge;
2703
2704         }// End persistAaiEdgeWithDepParams()
2705         
2706         /**
2707          * Persist aai edge with dep params.
2708          *
2709          * @param transId the trans id
2710          * @param fromAppId the from app id
2711          * @param graph the graph
2712          * @param startVert the start vert
2713          * @param targetNodeType the target node type
2714          * @param targetNodeParamHash the target node param hash
2715          * @return the titan edge
2716          * @throws AAIException the AAI exception
2717          */
2718         @Deprecated
2719         public static TitanEdge persistAaiEdgeWithDepParams( String transId, String fromAppId, TitanTransaction graph,  
2720                         TitanVertex startVert, String targetNodeType, HashMap <String,Object> targetNodeParamHash) throws AAIException{
2721                 return persistAaiEdgeWithDepParams(  transId,  fromAppId,  graph,  
2722                                  startVert,  targetNodeType,  targetNodeParamHash, null);
2723         }
2724
2725         /**
2726          * Gets the node key prop hash.
2727          *
2728          * @param transId the trans id
2729          * @param fromAppId the from app id
2730          * @param graph the graph
2731          * @param vtx the vtx
2732          * @return nodeKeyPropHash
2733          * @throws AAIException the AAI exception
2734          */
2735         public static HashMap <String, Object> getNodeKeyPropHash( String transId, String fromAppId, TitanTransaction graph, TitanVertex vtx) throws AAIException{
2736
2737                 if( vtx == null ){
2738                         throw new AAIException("AAI_6109", "null node object passed to getNodeKeyPropHash().");
2739                 }
2740                 
2741                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
2742                 
2743                 String nType = vtx.<String>property("aai-node-type").orElse(null);
2744                 if( ! dbMaps.NodeKeyProps.containsKey(nType) ){
2745                         // Problem if no key Properties defined for this nodeType
2746                         String defVer = AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP);
2747                         throw new AAIException("AAI_6105", "No node-key-properties defined in dbMaps for nodeType = " + nType + " (ver=" + defVer + ")"); 
2748                 }
2749
2750                 HashMap <String,Object>nodeKeyPropsHash = new HashMap<String,Object>();
2751                 Collection <String> keyProps =  dbMaps.NodeKeyProps.get(nType);
2752                 Iterator <String> keyPropI = keyProps.iterator();
2753                 while( keyPropI.hasNext() ){
2754                         String propName = keyPropI.next();
2755                         Object value = (Object) vtx.<Object>property(propName).orElse(null); 
2756                         nodeKeyPropsHash.put(propName, value);
2757                 }
2758
2759                 return nodeKeyPropsHash;
2760
2761         }// End of getNodeKeyPropHash()
2762
2763         /**
2764          * Gets the node name prop hash.
2765          *
2766          * @param transId the trans id
2767          * @param fromAppId the from app id
2768          * @param graph the graph
2769          * @param vtx the vtx
2770          * @param apiVersion the api version
2771          * @return nodeKeyPropHash
2772          * @throws AAIException the AAI exception
2773          */
2774         public static HashMap <String, Object> getNodeNamePropHash( String transId, String fromAppId, TitanTransaction graph, TitanVertex vtx, String apiVersion) throws AAIException{
2775
2776                 if( vtx == null ){
2777                         throw new AAIException("AAI_6109", "null node object passed to getNodeNamePropHash()." );
2778                 }
2779
2780                 String nType = vtx.<String>property("aai-node-type").orElse(null);
2781                 HashMap <String,Object>nodeNamePropsHash = new HashMap<String,Object>();
2782                 Collection <String> keyProps =  DbMeth.getNodeNameProps(transId, fromAppId, nType, apiVersion);
2783                 Iterator <String> keyPropI = keyProps.iterator();
2784                 while( keyPropI.hasNext() ){
2785                         String propName = keyPropI.next();
2786                         Object value = (Object) vtx.<Object>property(propName).orElse(null); 
2787                         nodeNamePropsHash.put(propName, value);
2788                 }
2789
2790                 return nodeNamePropsHash;
2791
2792         }// End of getNodeNamePropHash()
2793         
2794
2795         /**
2796          * Removes the aai edge.
2797          *
2798          * @param transId the trans id
2799          * @param fromAppId the from app id
2800          * @param graph the graph
2801          * @param tEdge the t edge
2802          * @return void
2803          */
2804         public static void removeAaiEdge( String transId, String fromAppId, TitanTransaction graph, TitanEdge tEdge){
2805                 // Before removing the edge, touch the vertices on each side so their resource-versions will get updated 
2806                 TitanVertex tmpVIn = tEdge.inVertex();
2807                 touchVertex( transId, fromAppId, tmpVIn );
2808                  
2809                 TitanVertex tmpVOut = tEdge.outVertex();
2810                 touchVertex( transId, fromAppId, tmpVOut );
2811
2812                 // Remove the passed in edge.
2813                 tEdge.remove();
2814
2815         }// end of removeAaiEdge()
2816
2817
2818         /**
2819          * Removes the aai node.
2820          *
2821          * @param transId the trans id
2822          * @param fromAppId the from app id
2823          * @param graph the graph
2824          * @param thisVtx the this vtx
2825          * @param scopeParam the scope param
2826          * @param apiVersion the api version
2827          * @param resourceVersion the resource version
2828          * @throws AAIException the AAI exception
2829          */
2830         public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, 
2831                         String apiVersion, String resourceVersion ) throws AAIException{
2832                 // Note: the resource Version Override flag is only set to true when called by the Model Delete code which
2833                 //    has no way to know the resource-versions of nodes at lower-levels of it's model topology.
2834                 Boolean resVersionOverrideFlag = false;
2835                 removeAaiNode( transId, fromAppId, graph, thisVtx, scopeParam, apiVersion, resourceVersion, resVersionOverrideFlag );
2836         }
2837         
2838
2839         /**
2840          *  <pre>
2841          *  Possible values for deleteScope can be:
2842          *      USE_DEFAULT - Get the scope from ref data for this node
2843          *      THIS_NODE_ONLY (but should fail if it there are nodes that depend on it for uniqueness)
2844          *      CASCADE_TO_CHILDREN  - will look for OUT-Edges that have parentOf/hasDelTarget = true and follow those down
2845          *      ERROR_4_IN_EDGES_OR_CASCADE - combo of error-if-any-IN-edges + CascadeToChildren
2846          *      ERROR_IF_ANY_IN_EDGES - Fail if this node has any existing IN edges 
2847          *      ERROR_IF_ANY_EDGES - Fail if this node has any existing edges at all!
2848          *  </pre>.
2849          *
2850          * @param transId the trans id
2851          * @param fromAppId the from app id
2852          * @param graph the graph
2853          * @param thisVtx the this vtx
2854          * @param scopeParam the scope param
2855          * @param apiVersion the api version
2856          * @param resourceVersion the resource version
2857          * @param resVerOverride the res ver override
2858          * @return void
2859          * @throws AAIException the AAI exception
2860          */
2861         public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, 
2862                         String apiVersion, String resourceVersion, Boolean resVerOverride ) throws AAIException{
2863                 String nodeType2Del = thisVtx.<String>property("aai-node-type").orElse(null);
2864                 String deleteScope = scopeParam;
2865                 if( scopeParam.equals("USE_DEFAULT") ){
2866                         deleteScope =   getDefaultDeleteScope(transId, fromAppId, nodeType2Del, apiVersion);
2867                 }
2868                 
2869                 if( !resVerOverride && needToDoResourceVerCheck(apiVersion, false) ){
2870                         // Need to check that they knew what they were deleting
2871                         String existingResVer = thisVtx.<String>property("resource-version").orElse(null);
2872                         if( resourceVersion == null || resourceVersion.equals("") ){
2873                                 throw new AAIException("AAI_6130", "Resource-version not passed for delete of = " + nodeType2Del); 
2874                         }
2875                         else if( (existingResVer != null) && !resourceVersion.equals(existingResVer) ){
2876                                 throw new AAIException("AAI_6131", "Resource-version MISMATCH for delete of = " + nodeType2Del); 
2877                         }
2878                 }
2879
2880                 if( !deleteScope.equals("THIS_NODE_ONLY")
2881                                 && !deleteScope.equals("CASCADE_TO_CHILDREN")
2882                                 && !deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE")
2883                                 && !deleteScope.equals("ERROR_IF_ANY_EDGES")
2884                                 && !deleteScope.equals("ERROR_IF_ANY_IN_EDGES") ){
2885                         throw new AAIException("AAI_6120", "Unrecognized value in deleteScope:  [" + deleteScope + "]."); 
2886                 }
2887
2888                 if( deleteScope.equals("ERROR_IF_ANY_EDGES") ){
2889                         if ( thisVtx.edges(Direction.BOTH).hasNext() ) {
2890                                 throw new AAIException("AAI_6110", "Node cannot be deleted because it still has Edges and the ERROR_IF_ANY_EDGES scope was used."); 
2891                         }
2892                 } 
2893                 else if( deleteScope.equals("ERROR_IF_ANY_IN_EDGES") || deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE") ){
2894                         Iterator <Edge> eI = thisVtx.edges(Direction.IN);
2895                         boolean onlyHasParent = false;
2896                         Edge temp = null;
2897                         if( eI != null && eI.hasNext() ){
2898                                 temp = eI.next();
2899                                 Boolean isParent = temp.<Boolean>property("isParent").orElse(null);
2900                                 if (isParent != null && isParent && !eI.hasNext()) {
2901                                         onlyHasParent = true;
2902                                 }
2903                                 
2904                                 if (!onlyHasParent) {
2905                                         throw new AAIException("AAI_6110", "Node cannot be deleted because it still has Edges and the " + deleteScope + " scope was used.");
2906                                 }
2907                         }
2908                 }
2909                 else if( deleteScope.equals("THIS_NODE_ONLY")){
2910                         // Make sure nobody depends on this node.
2911                         Iterator<Edge> eI = thisVtx.edges(Direction.BOTH);
2912                         while( eI.hasNext() ){
2913                                 TitanEdge ed = (TitanEdge) eI.next();
2914                                 TitanVertex otherVtx = (TitanVertex) ed.otherVertex(thisVtx);
2915                                 String nodeTypeA = otherVtx.<String>property("aai-node-type").orElse(null);
2916                                 if( nodeTypeACanDependOnB(transId, fromAppId, nodeTypeA, nodeType2Del, apiVersion)){
2917                                         // We're only supposed to delete this node - but another node is dependant on it,
2918                                         // so we shouldn't delete this one.
2919                                         throw new AAIException("AAI_6110", "Node cannot be deleted using scope = " + deleteScope + 
2920                                                         " another node (type = " + nodeTypeA + ") depends on it for uniqueness."); 
2921                                 }
2922                         }
2923                 }
2924
2925                 // We've passed our checks - so do some deleting of edges and maybe pass 
2926                 //     the delete request down to children or delete-targets.
2927
2928                 // First we deal with the "IN"-Edges which can't have children/delete-targets which
2929                 // by definition (of "IN") on the other end
2930                 Iterator <Edge> eI_In = thisVtx.edges(Direction.IN);
2931                 while( eI_In.hasNext() ){
2932                         TitanEdge ed = (TitanEdge) eI_In.next();
2933                         
2934                         //- "touch" vertex on other side of this edge so it gets a fresh resource-version
2935                         TitanVertex tmpVOther = ed.otherVertex(thisVtx);
2936                         touchVertex( transId, fromAppId, tmpVOther );
2937                         
2938                         ed.remove();
2939                 }
2940
2941                 // Now look at the "OUT"-edges which might include children or delete-targets
2942                 String cascadeMsg = "This nt = " + nodeType2Del + ", Cascading del to: ";
2943                 Iterator <Edge> eI_Out = thisVtx.edges(Direction.OUT);
2944                 if( !eI_Out.hasNext() ){
2945                         cascadeMsg = cascadeMsg + "[no children for this node]";
2946                 }
2947                 while( eI_Out.hasNext() ){
2948                         TitanEdge ed = (TitanEdge) eI_Out.next();
2949                         
2950                         // "touch" vertex on other side of this edge so it gets a fresh resource-version
2951                         TitanVertex tmpVOther = ed.otherVertex(thisVtx);
2952                         touchVertex( transId, fromAppId, tmpVOther );
2953
2954                         Boolean otherVtxAChild = ed.<Boolean>property("isParent").orElse(null);
2955                         if( otherVtxAChild == null ){
2956                                 otherVtxAChild = false;
2957                         }
2958
2959                         Boolean otherVtxADeleteTarget = ed.<Boolean>property("hasDelTarget").orElse(null);
2960                         if( otherVtxADeleteTarget == null ){
2961                                 otherVtxADeleteTarget = false;
2962                         }
2963
2964                         if( (otherVtxAChild || otherVtxADeleteTarget) && 
2965                                         (deleteScope.equals("CASCADE_TO_CHILDREN") || deleteScope.equals("ERROR_4_IN_EDGES_OR_CASCADE")) ){
2966                                 // Delete the edge to the child and Pass the delete down to it.
2967                                 ed.remove();
2968                                 TitanVertex otherVtx = (TitanVertex) ed.otherVertex(thisVtx);
2969                                 String vid = otherVtx.id().toString();
2970                                 String nty = otherVtx.<String>property("aai-node-type").orElse(null);
2971                                 String resVers = otherVtx.<String>property("resource-version").orElse(null);
2972                                 cascadeMsg = cascadeMsg + "[" + nty + ":" + vid + "]";
2973                                 removeAaiNode(transId, fromAppId, graph, otherVtx, "CASCADE_TO_CHILDREN", apiVersion, resVers);
2974                         }
2975                         else {
2976                                 // The other node is not a child or deleteTarget.  Delete the edge to it if it is not
2977                                 // dependent (Should never be dependent since it's not a child/delTarget...  but 
2978                                 // someone could create a node that was dependent for Uniqueness without
2979                                 // being a child/target.
2980
2981                                 // DEBUG -- eventually add the check for dependancy that isn't on a parent-type or delTarget-type edge
2982                                 ed.remove();
2983                         }
2984                 }
2985                 
2986                 LOGGER.info(cascadeMsg);
2987
2988                 Iterator<Edge> eI = thisVtx.edges(Direction.BOTH);
2989                 if( ! eI.hasNext() ){
2990                         // By this point, either there were no edges to deal with, or we have dealt with them.
2991                         thisVtx.remove();
2992                 }
2993                 else {
2994                         // Something went wrong and we couldn't delete all the edges for this guy.
2995                         throw new AAIException("AAI_6110", "Node could be deleted because it unexpectedly still has Edges.\n"); 
2996                 }
2997         }
2998         
2999         
3000         /**
3001          * Removes the aai node.
3002          *
3003          * @param transId the trans id
3004          * @param fromAppId the from app id
3005          * @param graph the graph
3006          * @param thisVtx the this vtx
3007          * @param scopeParam the scope param
3008          * @return void
3009          * @throws AAIException the AAI exception
3010          */
3011         @Deprecated
3012         public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam) throws AAIException{
3013                 removeAaiNode(transId, fromAppId, graph, thisVtx, scopeParam, null, null);
3014         }
3015         
3016         /**
3017          * Removes the aai node.
3018          *
3019          * @param transId the trans id
3020          * @param fromAppId the from app id
3021          * @param graph the graph
3022          * @param thisVtx the this vtx
3023          * @param scopeParam the scope param
3024          * @param apiVersion the api version
3025          * @throws AAIException the AAI exception
3026          */
3027         @Deprecated
3028         public static void removeAaiNode( String transId, String fromAppId, TitanTransaction graph, TitanVertex thisVtx, String scopeParam, 
3029                         String apiVersion ) throws AAIException{
3030                 removeAaiNode(transId, fromAppId, graph, thisVtx, scopeParam, apiVersion, null);
3031         }
3032         // end of removeAaiNode()
3033
3034
3035         /**
3036          * Delete all graph data.
3037          *
3038          * @param transId the trans id
3039          * @param fromAppId the from app id
3040          * @param graph the graph
3041          * @return void
3042          */
3043         public static void deleteAllGraphData( String transId, String fromAppId, TitanGraph graph ){
3044                 /** ======================================================================
3045                  * WARNING -- this removes ALL the data that is currently in the graph.
3046                  * ======================================================================
3047                  **/
3048                  LOGGER.warn("deleteAllGraphData called! Run for the hills!");
3049                 Iterator<Edge> edges = graph.edges(Direction.BOTH);
3050                 graph.tx().commit();
3051                 Edge edge = null;
3052                 while (edges.hasNext()) {
3053                         edge = edges.next();
3054                         edges.remove();
3055                 }
3056                 graph.tx().commit();
3057                 Iterator<Vertex> vertices = graph.vertices();
3058                 graph.tx().commit();
3059                 Vertex vertex = null;
3060                 while (vertices.hasNext()) {
3061                         vertex = vertices.next();
3062                         vertex.remove();
3063                 }
3064                 graph.tx().commit();
3065         }
3066
3067
3068         /**
3069          * Show all edges for node.
3070          *
3071          * @param transId the trans id
3072          * @param fromAppId the from app id
3073          * @param tVert the t vert
3074          * @return the array list
3075          */
3076         public static ArrayList <String> showAllEdgesForNode( String transId, String fromAppId, TitanVertex tVert ){ 
3077
3078                 ArrayList <String> retArr = new ArrayList <String> ();
3079                 Iterator <Edge> eI = tVert.edges(Direction.IN);
3080                 if( ! eI.hasNext() ){
3081                         retArr.add("No IN edges were found for this vertex. ");
3082                 }
3083                 while( eI.hasNext() ){
3084                         TitanEdge ed = (TitanEdge) eI.next();
3085                         String lab = ed.label();
3086                         TitanVertex vtx = (TitanVertex) ed.otherVertex(tVert);
3087                         if( vtx == null ){
3088                                 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
3089                         }
3090                         else {
3091                                 String nType = vtx.<String>property("aai-node-type").orElse(null);
3092                                 String vid = vtx.id().toString();
3093                                 retArr.add("Found an IN edge (" + lab + ") to this vertex from a [" + nType + "] node with VtxId = " + vid );
3094                                 //DEBUG ---
3095                                 //showPropertiesForEdge(  transId, fromAppId, ed );
3096                         }
3097                 }
3098                 
3099                 eI = tVert.edges(Direction.OUT);
3100                 if( ! eI.hasNext() ){
3101                         retArr.add("No OUT edges were found for this vertex. ");
3102                 }
3103                 while( eI.hasNext() ){
3104                         TitanEdge ed = (TitanEdge) eI.next();
3105                         String lab = ed.label();
3106                         TitanVertex vtx = (TitanVertex) ed.otherVertex(tVert);
3107                         if( vtx == null ){
3108                                 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
3109                         }
3110                         else {
3111                                 String nType = vtx.<String>property("aai-node-type").orElse(null);
3112                                 String vid = vtx.id().toString();
3113                                 retArr.add("Found an OUT edge (" + lab + ") from this vertex to a [" + nType + "] node with VtxId = " + vid );
3114                                 //DEBUG ---
3115                                 //showPropertiesForEdge(  transId, fromAppId, ed );
3116                         }
3117                 }
3118                 return retArr;
3119         }
3120
3121         
3122         /**
3123          * Show properties for node.
3124          *
3125          * @param transId the trans id
3126          * @param fromAppId the from app id
3127          * @param tVert the t vert
3128          * @return the array list
3129          */
3130         public static ArrayList <String> showPropertiesForNode( String transId, String fromAppId, TitanVertex tVert ){ 
3131
3132                 ArrayList <String> retArr = new ArrayList <String> ();
3133                 if( tVert == null ){
3134                         retArr.add("null Node object passed to showPropertiesForNode()\n");
3135                 }
3136                 else {
3137                         String nodeType = "";
3138                         //String datType = "";
3139                         Object ob = tVert.<Object>property("aai-node-type").orElse(null);
3140                         if( ob == null ){
3141                                 nodeType = "null";
3142                         }
3143                         else{
3144                                 nodeType = ob.toString();
3145                                 //datType = ob.getClass().getSimpleName();
3146                         }
3147                         
3148                         retArr.add(" AAINodeType/VtxID for this Node = [" + nodeType + "/" + tVert.id() + "]");
3149                         retArr.add(" Property Detail: ");
3150                         Iterator<VertexProperty<Object>> pI = tVert.properties();
3151                         while( pI.hasNext() ){
3152                                 VertexProperty<Object> tp = pI.next();
3153                                 Object val = tp.value();
3154                                 //retArr.add("Prop: [" + tp.getPropertyKey() + "], val = [" + val + "], dataType = " + val.getClass() );
3155                                 retArr.add("Prop: [" + tp.key() + "], val = [" + val + "] ");
3156                         }
3157                 }
3158                 return retArr;
3159         }
3160
3161         
3162         /**
3163          * Gets the node name props.
3164          *
3165          * @param transId the trans id
3166          * @param fromAppId the from app id
3167          * @param nodeType the node type
3168          * @param apiVersion the api version
3169          * @return HashMap of keyProperties
3170          * @throws AAIException the AAI exception
3171          */
3172         public static Collection <String> getNodeNameProps( String transId, String fromAppId, String nodeType, String apiVersion ) throws AAIException{
3173
3174                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
3175                 
3176                 Collection <String> nameProps = new ArrayList <String>();
3177                 if( dbMaps.NodeNameProps.containsKey(nodeType) ){
3178                         nameProps = dbMaps.NodeNameProps.get(nodeType);
3179                 }
3180                 else if( DbEdgeRules.NodeTypeCategory.containsKey(nodeType) ){
3181                         // The passed-in nodeType was really a nodeCategory, theoretically, all the guys in the same 
3182                         // category should have the same name property -- so if they just give us the category, we will
3183                         // just give the name info from the first nodeType we encounter of that category.
3184                         Collection <String> nTypeCatCol = DbEdgeRules.NodeTypeCategory.get(nodeType);
3185                         Iterator <String> catItr = nTypeCatCol.iterator();
3186                         String catInfo = "";
3187                         if( catItr.hasNext() ){
3188                                 // For now, we only look for one.
3189                                 catInfo = catItr.next();
3190                         }
3191                         else {
3192                                 throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + nodeType); 
3193                         }
3194
3195                         String [] flds = catInfo.split(",");
3196                         if( flds.length != 4 ){
3197                                 throw new AAIException("AAI_6121", "Bad EdgeRule.NodeTypeCategory data (itemCount=" + flds.length + ") for nodeType = [" + nodeType + "]."); 
3198                         }
3199
3200                         String nodeTypesString = flds[0];
3201                         String [] nodeTypeNames = nodeTypesString.split("\\|");
3202                         if( nodeTypeNames != null && nodeTypeNames.length > 0 ){
3203                                 // We'll just use the first one
3204                                 String nt = nodeTypeNames[0];
3205                                 nameProps = dbMaps.NodeNameProps.get(nt);
3206                         }
3207                 }
3208                 
3209                 
3210                 // Note - it's ok if there was no defined name property for this nodeType.
3211                 
3212                 return nameProps;
3213
3214         }// end of getNodeKeyPropNames
3215         
3216         
3217         /**
3218          * Gets the edge tag prop put hash 4 rule.
3219          *
3220          * @param transId the trans id
3221          * @param fromAppId the from app id
3222          * @param edRule the ed rule
3223          * @return the edge tag prop put hash 4 rule
3224          * @throws AAIException the AAI exception
3225          */
3226         public static HashMap <String,Object> getEdgeTagPropPutHash4Rule( String transId, String fromAppId, String edRule ) 
3227                         throws AAIException{ 
3228                 // For a given edgeRule - already pulled out of DbEdgeRules.EdgeRules --  parse out the "tags" that 
3229                 //     need to be set for this kind of edge.  
3230                 // These are the Boolean properties like, "isParent", "usesResource" etc.  
3231                 HashMap <String,Object> retEdgePropPutMap = new HashMap <String,Object>();
3232                 
3233                 if( (edRule == null) || edRule.equals("") ){
3234                         // No edge rule found for this
3235                         throw new AAIException("AAI_6120", "blank edRule passed to getEdgeTagPropPutHash4Rule()"); 
3236                 }
3237                         
3238                 int tagCount = DbEdgeRules.EdgeInfoMap.size();
3239                 String [] rules = edRule.split(",");
3240                 if( rules.length != tagCount ){
3241                         throw new AAIException("AAI_6121", "Bad EdgeRule data (itemCount =" + rules.length + ") for rule = [" + edRule  + "]."); 
3242                 }
3243
3244                 // In DbEdgeRules.EdgeRules -- What we have as "edRule" is a comma-delimited set of strings.
3245                 // The first item is the edgeLabel.
3246                 // The second in the list is always "direction" which is always OUT for the way we've implemented it.
3247                 // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to 
3248                 // tags as defined in EdgeInfoMap.
3249                 // Note - if they are tagged as 'reverse', that means they get the tag name with "-REV" on it
3250                 for( int i = DbEdgeRules.firstTagIndex; i < tagCount; i++ ){
3251                         String booleanStr = rules[i];
3252                         Integer mapKey = new Integer(i);
3253                         String propName = DbEdgeRules.EdgeInfoMap.get(mapKey);
3254                         String revPropName = propName + "-REV";
3255                         
3256                         if( booleanStr.equals("true") ){
3257                                 retEdgePropPutMap.put(propName, true);
3258                                 retEdgePropPutMap.put(revPropName,false);
3259                         }
3260                         else if( booleanStr.equals("false") ){
3261                                 retEdgePropPutMap.put(propName, false);
3262                                 retEdgePropPutMap.put(revPropName,false);
3263                         }
3264                         else if( booleanStr.equals("reverse") ){
3265                                 retEdgePropPutMap.put(propName, false);
3266                                 retEdgePropPutMap.put(revPropName,true);
3267                         }
3268                         else {
3269                                 throw new AAIException("AAI_6121", "Bad EdgeRule data for rule = [" + edRule + "], val = [" + booleanStr + "]."); 
3270                         }
3271                         
3272                 }
3273
3274                 return retEdgePropPutMap;
3275                 
3276         } // End of getEdgeTagPropPutHash()
3277
3278
3279         
3280         /**
3281          * Gets the edge tag prop put hash.
3282          *
3283          * @param transId the trans id
3284          * @param fromAppId the from app id
3285          * @param edgeRuleKey the edge rule key
3286          * @return the edge tag prop put hash
3287          * @throws AAIException the AAI exception
3288          */
3289         public static Map<String, EdgeRule> getEdgeTagPropPutHash( String transId, String fromAppId, String edgeRuleKey ) 
3290                         throws AAIException{ 
3291                 // For a given edgeRuleKey (nodeTypeA|nodeTypeB), look up the rule that goes with it in
3292                 // DbEdgeRules.EdgeRules and parse out the "tags" that need to be set on each edge.  
3293                 // These are the Boolean properties like, "isParent", "usesResource" etc.  
3294                 // Note - this code is also used by the updateEdgeTags.java code
3295
3296                 String[] edgeRuleKeys = edgeRuleKey.split("\\|");
3297                 
3298                 if (edgeRuleKeys.length < 2 || ! EdgeRules.getInstance().hasEdgeRule(edgeRuleKeys[0], edgeRuleKeys[1])) {
3299                         throw new AAIException("AAI_6120", "Could not find an DbEdgeRule entry for passed edgeRuleKey (nodeTypeA|nodeTypeB): " + edgeRuleKey + "."); 
3300                 }
3301                 
3302                 Map<String, EdgeRule> edgeRules = EdgeRules.getInstance().getEdgeRules(edgeRuleKeys[0], edgeRuleKeys[1]);
3303                 
3304                 return edgeRules;
3305                 
3306         } // End of getEdgeTagPropPutHash()
3307
3308         
3309         /**
3310          * This property was put by newer version of code.
3311          *
3312          * @param apiVersionStr the api version str
3313          * @param nodeType the node type
3314          * @param propName the prop name
3315          * @return true, if successful
3316          * @throws AAIException the AAI exception
3317          */
3318         private static boolean  thisPropertyWasPutByNewerVersionOfCode( String apiVersionStr, 
3319                                         String nodeType, String propName) throws AAIException{
3320                 // We want to return True if the nodeType + property-name combo was introduced AFTER the apiVersion passed.
3321                 
3322                 int apiVerInt = 0;
3323                 int propIntroVerInt = 0;
3324                 
3325                 if( apiVersionStr == null || apiVersionStr.equals("") ){
3326                         apiVersionStr = org.openecomp.aai.util.AAIApiVersion.get();
3327                 }
3328                 apiVerInt = getVerNumFromVerString(apiVersionStr);
3329                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
3330                 String propIntroKey = nodeType + "|" + propName;
3331                 if( propName.equals("prov-status") ){
3332                         // This is a special case -- The dbMaps from v2 has it in there, but it was introduced half way through.  So
3333                         // it needs to be catogorized as v3.
3334                         propIntroVerInt = 3;
3335                 }
3336                 else if( ! dbMaps.PropertyVersionInfoMap.containsKey(propIntroKey) ){
3337                         String detail = propIntroKey + " [" + propIntroKey + "] not found in dbMaps.PropertyVersionInfoMap."; 
3338                         throw new AAIException("AAI_6121", detail); 
3339                 }
3340                 else {
3341                         String propIntroVerString = dbMaps.PropertyVersionInfoMap.get(propIntroKey);
3342                         propIntroVerInt = getVerNumFromVerString( propIntroVerString );
3343                 }
3344                 
3345                 if( propIntroVerInt > apiVerInt ){
3346                         return true;
3347                 }
3348                 else {
3349                         return false;
3350                 }
3351                 
3352         } // End of thisPropertyWasPutByNewerVersionOfCode()
3353
3354         
3355         /**
3356          * Touch vertex.
3357          *
3358          * @param transId the trans id
3359          * @param fromAppId the from app id
3360          * @param v the v
3361          * @return void
3362          */
3363         public static void touchVertex( String transId, String fromAppId, TitanVertex v ){
3364                 // We want to "touch" the vertex -- Ie. update it's last-mod-date, last-mod- resource-version to the current date/time
3365                 if( v != null ){
3366                         long unixTimeNow = System.currentTimeMillis() / 1000L;
3367                         String timeNowInSec = "" + unixTimeNow;
3368                         v.property( "aai-last-mod-ts", timeNowInSec );
3369                         v.property( "resource-version", timeNowInSec );
3370                         v.property( "last-mod-source-of-truth", fromAppId );
3371                 }
3372         } // End of touchVertex()
3373         
3374         
3375         /**
3376          * Check prop cardinality.
3377          *
3378          * @param propName the prop name
3379          * @param cardinalityType the cardinality type
3380          * @return boolean
3381          * @throws AAIException the AAI exception
3382          */
3383         public static boolean checkPropCardinality( String propName, String cardinalityType ) throws AAIException {
3384                 
3385                 // Return true if the named property is tagged in our dbMaps PropetyDataTypeMap as 
3386                 // having the passed in cardinality type.  
3387                 // NOTE: supported cardinality types in dbMaps = "Set" or "List"
3388                 // In Titan (and ex5.json), those go in as "SET" and "LIST"
3389                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
3390                 
3391                 if( dbMaps.PropertyDataTypeMap.containsKey(propName) ){
3392                         String propDataType = dbMaps.PropertyDataTypeMap.get(propName);
3393                         if( propDataType != null && propDataType.startsWith(cardinalityType) ){
3394                                 return true;
3395                         }
3396                 }
3397                 return false;
3398                 
3399         } // End of checkPropCardinality()
3400         
3401         /**
3402          * Convert type if needed.
3403          *
3404          * @param propName the prop name
3405          * @param val the val
3406          * @return convertedValue (if it was a String but needed to be a Boolean)
3407          * @throws AAIException the AAI exception
3408          */
3409         public static Object convertTypeIfNeeded( String propName, Object val )
3410                         throws AAIException {
3411                 // Make sure the dataType of the passed-in Object matches what the DB expects
3412                 
3413                 // NOTE: since this is a fix very late in our dev cycle, we'll just fix the scenarios that
3414                 //   we're having trouble with which is Strings getting into the db which should be going in as 
3415                 //   Booleans or Integers.
3416                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
3417                 
3418                 if( dbMaps.PropertyDataTypeMap.containsKey(propName) ){
3419                         String dbExpectedDataType = dbMaps.PropertyDataTypeMap.get(propName);
3420                         if( dbExpectedDataType != null 
3421                                         && dbExpectedDataType.equals("Boolean") 
3422                                         && val != null
3423                                         && !(val instanceof Boolean) ){
3424                                 String valStr = val.toString().trim();
3425                                 if( valStr.equals("true") || valStr.equals("True") || valStr.equals("TRUE") ){
3426                                         return Boolean.valueOf("true");
3427                                 }
3428                                 else if( valStr.equals("false") || valStr.equals("False") || valStr.equals("FALSE") ){
3429                                         return Boolean.valueOf("false");
3430                                 }
3431                                 else {
3432                                         String emsg = "Error trying to convert value: [" + valStr + "] to a Boolean for property + " + propName + "\n";
3433                                         throw new AAIException("AAI_6120", emsg); 
3434                                 }
3435                         }
3436                         else if( dbExpectedDataType != null 
3437                                         && dbExpectedDataType.equals("Integer") 
3438                                         && val != null 
3439                                         && !(val.toString().trim().equals("")) 
3440                                         && !(val instanceof Integer) ){
3441                                 String valStr = val.toString().trim();
3442                                 Integer newInt;
3443                                 try {
3444                                         newInt = Integer.valueOf(valStr);
3445                                         return newInt;
3446                                 }
3447                                 catch( Exception e ){
3448                                         String emsg = "Error trying to convert value: [" + valStr + "] to an Integer for property + " + propName + "\n";
3449                                         throw new AAIException("AAI_6120", emsg); 
3450                                 }
3451                         }
3452                 }
3453                 
3454                 // If it didn't need to be converted, just return it.
3455                 return val;
3456         
3457         } // End of convertTypeIfNeeded()
3458
3459         
3460         
3461         /**
3462          * This vertex not reachable.
3463          *
3464          * @param transId the trans id
3465          * @param fromAppId the from app id
3466          * @param graph the graph
3467          * @param v the v
3468          * @param version the version
3469          * @return boolean
3470          */
3471         public static boolean thisVertexNotReachable( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version){
3472                 if( v == null ){
3473                         return true;   
3474                 }
3475                 else {
3476                         try {
3477                                  v.id().toString();
3478                         }
3479                         catch( Exception ex ){
3480                                 // Could not get this -- sometimes we're holding a vertex object that has gotten deleted, so
3481                                 // when we try to get stuff from it, we get an "Element Has Been Removed" error from Titan
3482                                 return true;
3483                         }
3484                 }
3485                 
3486                 return false;
3487                 
3488         } // End of thisVertexNotReachable()
3489
3490         /**
3491          * This vertex has bad edges.
3492          *
3493          * @param transId the trans id
3494          * @param fromAppId the from app id
3495          * @param graph the graph
3496          * @param v the v
3497          * @param version the version
3498          * @return boolean
3499          */
3500         public static boolean thisVertexHasBadEdges( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version){
3501                 
3502                 Iterator <Edge> eItor = v.edges(Direction.BOTH);
3503                 while( eItor.hasNext() ){
3504                         Edge e = null;
3505                         e = eItor.next();
3506                         if( e == null ){
3507                                 return true;
3508                         }
3509                         Vertex vIn = e.inVertex();
3510                         if( (vIn == null) || (vIn.<String>property("aai-node-type").orElse(null) == null) ){
3511                                  // this is a bad edge because it points to a vertex that isn't there anymore
3512                                  return true;
3513                         }
3514                         
3515                         Vertex vOut = e.outVertex();
3516                         if( (vOut == null) || (vOut.<String>property("aai-node-type").orElse(null) == null) ){
3517                                  // this is a bad edge because it points to a vertex that isn't there anymore
3518                                  return true;
3519                         }
3520                 }
3521                 
3522                 // If we made it to here, the vertex's edges must be ok.
3523                 return false;
3524                 
3525         } // End of thisVertexHasBadEdges()
3526         
3527         
3528         /**
3529          * This vertex is A phantom.
3530          *
3531          * @param transId the trans id
3532          * @param fromAppId the from app id
3533          * @param graph the graph
3534          * @param v the v
3535          * @param version the version
3536          * @return boolean
3537          * @throws AAIException the AAI exception
3538          */
3539         public static boolean thisVertexIsAPhantom( String transId, String fromAppId, TitanTransaction graph, TitanVertex v, String version ) 
3540                         throws AAIException {
3541                 
3542                 
3543                 // The kind of Phantom we're looking for is the kind that we sometimes get when we do a select without
3544                 // using key properties.  They can be in the database as a vertex, but the indexes that should point to 
3545                 // them are not working -- so they cannot be used by normal interfaces (like the REST API) which means
3546                 // that if we return it, it can mess up a caller who tries to use it.
3547                 if( v == null ){
3548                         return true;   
3549                 }
3550                 String thisVid =  v.id().toString();
3551                 
3552                 DbMaps dbMaps = IngestModelMoxyOxm.dbMapsContainer.get(AAIConfig.get(AAIConstants.AAI_DEFAULT_API_VERSION_PROP));
3553                 
3554                 Object propOb = v.<Object>property("aai-node-type").orElse(null);
3555                 if( propOb == null ){
3556                         // This vertex does not have an aai-node-type ---> it is messed up
3557                         return true;
3558                 }
3559                 String nType = propOb.toString();
3560                 if(  ! dbMaps.NodeKeyProps.containsKey(nType) ){
3561                         // This node Type does not have keys defined
3562                         // This could just be bad reference data, so we will not flag this guy, but we
3563                         // can't really do our test...
3564                         return false;
3565                 }
3566                 
3567                 HashMap <String,Object> propHashWithKeys = new HashMap<String, Object>();
3568                 Collection <String> keyProps = null;
3569                 try {
3570                         keyProps = getNodeKeyPropNames(transId, fromAppId, nType, version);
3571                 }
3572                 catch (AAIException ex) {
3573                         // something wrong with getting this guy's key property names - we'll abandon this test...
3574                         return false;
3575                 }
3576         
3577         Iterator <String> keyPropI = keyProps.iterator();
3578         while( keyPropI.hasNext() ){
3579                 String propName = keyPropI.next();
3580                 String propVal = "";
3581                 Object ob = v.<Object>property(propName).orElse(null);
3582                 if( ob != null ){
3583                         propVal = ob.toString();
3584                 }
3585                 propHashWithKeys.put(propName, propVal);
3586         }
3587         try {
3588                 // Note - We can get more than one back since some nodes need a dep. node for uniqueness.
3589                 //   We don't care about that -- we just want to make sure we can get this vertex back when
3590                 //   we're searching with it's indexed fields. 
3591                 // NOTE - we're passing the skipGroomCheck to getNodes so we don't wind up in an infinite loop
3592                 ArrayList <TitanVertex> vertList2 = getNodes( transId, fromAppId, graph, nType, propHashWithKeys, false, version, true ); 
3593                 Iterator<TitanVertex> iter2 = vertList2.iterator(); 
3594             while( iter2.hasNext() ){ 
3595                 TitanVertex tvx2 = iter2.next(); 
3596                 String foundId = tvx2.id().toString();
3597                 if( foundId.equals( thisVid ) ){
3598                         // We could get back the vertex by looking it up using key properties...  That's good.
3599                         return false;
3600                 }
3601             }
3602         }
3603         catch (Exception e2) {
3604                         //String msg = " Error encountered for this vertex id: [" + thisVid + 
3605                         //              "]. Caught this exception: " + e2.toString();
3606                         // Something messed up - but that doesn't prove that this is a phantom.
3607                         return false;
3608                 }
3609                 
3610         // If we dropped down to here, we have looked but could not pull the vertex out of the
3611         //    db using it's key fields, so it gets flagged as a Phantom.
3612                 return true;
3613         
3614         } // End of thisVertexIsAPhantom()
3615         
3616         
3617         /**
3618          * Gets the node by unique key.
3619          *
3620          * @param transId the trans id
3621          * @param fromAppId the from app id
3622          * @param graph the graph
3623          * @param aaiUniquekey the aai uniquekey
3624          * @return the node by unique key
3625          */
3626         public TitanVertex getNodeByUniqueKey(String transId, String fromAppId, TitanTransaction graph, String aaiUniquekey) {
3627                 
3628                 TitanVertex vert = null;
3629                 
3630                 Iterator<?> vertI =  graph.query().has("aai-unique-key", aaiUniquekey).vertices().iterator(); 
3631                                                 
3632                 if( vertI != null && vertI.hasNext()) {
3633                         // We found a vertex that meets the input criteria. 
3634                         vert = (TitanVertex) vertI.next();
3635                 }
3636                 
3637                 return vert;
3638         }
3639
3640
3641
3642 }
3643