Update license files, sonar plugin and fix tests
[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