Merge "Update shema for VFC"
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / dbgen / DataGrooming.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.onap.aai.dbgen;
23
24 import java.io.BufferedReader;
25 import java.io.BufferedWriter;
26 import java.io.File;
27 import java.io.FileReader;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.Properties;
39 import java.util.Set;
40 import java.util.UUID;
41
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
43 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
44 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
45 import org.apache.tinkerpop.gremlin.structure.Direction;
46 import org.apache.tinkerpop.gremlin.structure.Edge;
47 import org.apache.tinkerpop.gremlin.structure.Graph;
48 import org.apache.tinkerpop.gremlin.structure.Property;
49 import org.apache.tinkerpop.gremlin.structure.Vertex;
50 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
51 import org.onap.aai.db.props.AAIProperties;
52 import org.onap.aai.dbmap.AAIGraph;
53 import org.onap.aai.exceptions.AAIException;
54 import org.onap.aai.introspection.Introspector;
55 import org.onap.aai.introspection.Loader;
56 import org.onap.aai.introspection.LoaderFactory;
57 import org.onap.aai.introspection.ModelType;
58 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
59 import org.onap.aai.logging.ErrorLogHelper;
60 import org.onap.aai.serialization.db.AAIDirection;
61 import org.onap.aai.serialization.db.EdgeProperty;
62 import org.onap.aai.util.AAIConfig;
63 import org.onap.aai.util.AAIConstants;
64 import org.onap.aai.util.FormatDate;
65
66 import com.att.eelf.configuration.Configuration;
67 import com.att.eelf.configuration.EELFLogger;
68 import com.att.eelf.configuration.EELFManager;
69 import com.thinkaurelius.titan.core.TitanFactory;
70 import com.thinkaurelius.titan.core.TitanGraph;
71
72
73 public class DataGrooming {
74
75         private static EELFLogger LOGGER;
76         private static final String FROMAPPID = "AAI-DB";
77         private static final String TRANSID = UUID.randomUUID().toString();
78         private static int dupeGrpsDeleted = 0;
79         
80         /**
81          * The main method.
82          *
83          * @param args the arguments
84          */
85         public static void main(String[] args) {
86                 
87                 // Set the logging file properties to be used by EELFManager
88                 Properties props = System.getProperties();
89                 props.setProperty(Configuration.PROPERTY_LOGGING_FILE_NAME, AAIConstants.AAI_DATA_GROOMING_LOGBACK_PROPS);
90                 props.setProperty(Configuration.PROPERTY_LOGGING_FILE_PATH, AAIConstants.AAI_HOME_ETC_APP_PROPERTIES);
91                 LOGGER = EELFManager.getInstance().getLogger(DataGrooming.class);
92                 String ver = "version"; // Placeholder
93                 Boolean doAutoFix = false;
94                 Boolean edgesOnlyFlag = false;
95                 Boolean dontFixOrphansFlag = false;
96                 Boolean skipHostCheck = false;
97                 Boolean singleCommits = false;
98                 Boolean dupeCheckOff = false;
99                 Boolean dupeFixOn = false;
100                 Boolean ghost2CheckOff = false;
101                 Boolean ghost2FixOn = false;
102                 Boolean neverUseCache = false;
103                 Boolean skipEdgeCheckFlag = false;
104                 
105                 int timeWindowMinutes = 0; // A value of 0 means that we will not have a time-window -- we will look
106                                    // at all nodes of the passed-in nodeType. 
107                 long windowStartTime = 0; // Translation of the window into a starting timestamp 
108                 
109                 int maxRecordsToFix = AAIConstants.AAI_GROOMING_DEFAULT_MAX_FIX;
110                 int sleepMinutes = AAIConstants.AAI_GROOMING_DEFAULT_SLEEP_MINUTES;
111                 try {
112                         String maxFixStr = AAIConfig.get("aai.grooming.default.max.fix");
113                         if( maxFixStr != null &&  !maxFixStr.equals("") ){
114                                 maxRecordsToFix = Integer.parseInt(maxFixStr);
115                         }
116                         String sleepStr = AAIConfig.get("aai.grooming.default.sleep.minutes");
117                         if( sleepStr != null &&  !sleepStr.equals("") ){
118                                 sleepMinutes = Integer.parseInt(sleepStr);
119                         }
120                 }
121                 catch ( Exception e ){
122                         // Don't worry, we'll just use the defaults that we got from AAIConstants
123                         LOGGER.warn("WARNING - could not pick up aai.grooming values from aaiconfig.properties file. ");
124                 }
125                 
126                 String prevFileName = "";
127                 dupeGrpsDeleted = 0;
128                 FormatDate fd = new FormatDate("yyyyMMddHHmm", "GMT");
129                 String dteStr = fd.getDateTime();
130                 String groomOutFileName = "dataGrooming." + dteStr + ".out";
131
132                 String argString = "";
133                 for( int x = 0; x < args.length; x++ ) {
134                         argString = argString + " " + args[x];
135                 }
136                 LOGGER.info(" DataGrooming called with these options: [" + argString + "]");
137                 
138                 if (args.length > 0) {
139                         // They passed some arguments in that will affect processing
140                         
141                         for (int i = 0; i < args.length; i++) {
142                                 String thisArg = args[i];
143                                 if (thisArg.equals("-edgesOnly")) {
144                                         edgesOnlyFlag = true;
145                                 } else if (thisArg.equals("-autoFix")) {
146                                         doAutoFix = true;
147                                 } else if (thisArg.equals("-skipHostCheck")) {
148                                         skipHostCheck = true;
149                                 } else if (thisArg.equals("-dontFixOrphans")) {
150                                         dontFixOrphansFlag = true;
151                                 } else if (thisArg.equals("-singleCommits")) {
152                                         singleCommits = true;
153                                 } else if (thisArg.equals("-dupeCheckOff")) {
154                                         dupeCheckOff = true;
155                                 } else if (thisArg.equals("-dupeFixOn")) {
156                                         dupeFixOn = true;
157                                 } else if (thisArg.equals("-ghost2CheckOff")) {
158                                         ghost2CheckOff = true;
159                                 } else if (thisArg.equals("-neverUseCache")) {
160                                         neverUseCache = true;
161                                 } else if (thisArg.equals("-ghost2FixOn")) {
162                                         ghost2FixOn = true;
163                                 } else if (thisArg.equals("-skipEdgeChecks")) {
164                                         skipEdgeCheckFlag = true;
165                                 } else if (thisArg.equals("-maxFix")) {
166                                         i++;
167                                         if (i >= args.length) {
168                                                 LOGGER.error(" No value passed with -maxFix option.  ");
169                                                 System.exit(0);
170                                         }
171                                         String nextArg = args[i];
172                                         try {
173                                                 maxRecordsToFix = Integer.parseInt(nextArg);
174                                         } catch (Exception e) {
175                                                 LOGGER.error("Bad value passed with -maxFix option: ["
176                                                                                 + nextArg + "]");
177                                                 System.exit(0);
178                                         }
179                                 } else if (thisArg.equals("-sleepMinutes")) {
180                                         i++;
181                                         if (i >= args.length) {
182                                                 LOGGER.error("No value passed with -sleepMinutes option.");
183                                                 System.exit(0);
184                                         }
185                                         String nextArg = args[i];
186                                         try {
187                                                 sleepMinutes = Integer.parseInt(nextArg);
188                                         } catch (Exception e) {
189                                                 LOGGER.error("Bad value passed with -sleepMinutes option: ["
190                                                                                 + nextArg + "]");
191                                                 System.exit(0);
192                                         }
193                                 } else if (thisArg.equals("-timeWindowMinutes")) {
194                                         i++;
195                                         if (i >= args.length) {
196                                                 LOGGER.error("No value passed with -timeWindowMinutes option.");
197                                                 System.exit(0);
198                                         }
199                                         String nextArg = args[i];
200                                         try {
201                                                 timeWindowMinutes = Integer.parseInt(nextArg);
202                                         } catch (Exception e) {
203                                                 LOGGER.error("Bad value passed with -timeWindowMinutes option: ["
204                                                                                 + nextArg + "]");
205                                                 System.exit(0);
206                                         }
207                                         if( timeWindowMinutes > 0 ){
208                                                 // Translate the window value (ie. 30 minutes) into a unix timestamp like
209                                                 //    we use in the db - so we can select data created after that time.
210                                                 windowStartTime = figureWindowStartTime( timeWindowMinutes );
211                                         }
212                                 } else if (thisArg.equals("-f")) {
213                                         i++;
214                                         if (i >= args.length) {
215                                                 LOGGER.error(" No value passed with -f option. ");
216                                                 System.exit(0);
217                                         }
218                                         prevFileName = args[i];
219                                 } else {
220                                         LOGGER.error(" Unrecognized argument passed to DataGrooming: ["
221                                                                         + thisArg + "]. ");
222                                         LOGGER.error(" Valid values are: -f -autoFix -maxFix -edgesOnly -dupeFixOn -donFixOrphans -sleepMinutes -neverUseCache");
223                                         System.exit(0);
224                                 }
225                         }
226                 }
227                 
228
229                 try {
230                         LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
231
232                 }
233                 catch (Exception ex){
234                         LOGGER.error("ERROR - Could not create loader", ex);
235                         System.exit(1);
236                 }
237
238
239                 try {
240                         if (!prevFileName.equals("")) {
241                                 // They are trying to fix some data based on a data in a
242                                 // previous file.
243                                 LOGGER.info(" Call doTheGrooming() with a previous fileName ["
244                                                                 + prevFileName + "] for cleanup. ");
245                                 Boolean finalShutdownFlag = true;
246                                 Boolean cacheDbOkFlag = false;
247                                 doTheGrooming(prevFileName, edgesOnlyFlag, dontFixOrphansFlag,
248                                                 maxRecordsToFix, groomOutFileName, ver, singleCommits,
249                                                 dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
250                                                 finalShutdownFlag, cacheDbOkFlag, 
251                                                 skipEdgeCheckFlag, windowStartTime);
252                         } else if (doAutoFix) {
253                                 // They want us to run the processing twice -- first to look for
254                                 // delete candidates, then after
255                                 // napping for a while, run it again and delete any candidates
256                                 // that were found by the first run.
257                                 // Note: we will produce a separate output file for each of the
258                                 // two runs.
259                                 LOGGER.info(" Doing an auto-fix call to Grooming. ");
260                                 LOGGER.info(" First, Call doTheGrooming() to look at what's out there. ");
261                                 Boolean finalShutdownFlag = false;
262                                 Boolean cacheDbOkFlag = true;
263                                 int fixCandCount = doTheGrooming("", edgesOnlyFlag,
264                                                 dontFixOrphansFlag, maxRecordsToFix, groomOutFileName,
265                                                 ver, singleCommits, dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
266                                                 finalShutdownFlag, cacheDbOkFlag, 
267                                                 skipEdgeCheckFlag, windowStartTime);
268                                 if (fixCandCount == 0) {
269                                         LOGGER.info(" No fix-Candidates were found by the first pass, so no second/fix-pass is needed. ");
270                                 } else {
271                                         // We'll sleep a little and then run a fix-pass based on the
272                                         // first-run's output file.
273                                         try {
274                                                 LOGGER.info("About to sleep for " + sleepMinutes
275                                                                 + " minutes.");
276                                                 int sleepMsec = sleepMinutes * 60 * 1000;
277                                                 Thread.sleep(sleepMsec);
278                                         } catch (InterruptedException ie) {
279                                                 LOGGER.info("\n >>> Sleep Thread has been Interrupted <<< ");
280                                                 System.exit(0);
281                                         }
282
283                                         dteStr = fd.getDateTime();
284                                         String secondGroomOutFileName = "dataGrooming." + dteStr
285                                                         + ".out";
286                                         LOGGER.info(" Now, call doTheGrooming() a second time and pass in the name of the file "
287                                                                         + "generated by the first pass for fixing: ["
288                                                                         + groomOutFileName + "]");
289                                         finalShutdownFlag = true;
290                                         cacheDbOkFlag = false;
291                                         doTheGrooming(groomOutFileName, edgesOnlyFlag,
292                                                         dontFixOrphansFlag, maxRecordsToFix,
293                                                         secondGroomOutFileName, ver, singleCommits,
294                                                         dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
295                                                         finalShutdownFlag, cacheDbOkFlag, 
296                                                         skipEdgeCheckFlag, windowStartTime);
297                                 }
298                         } else {
299                                 // Do the grooming - plain vanilla (no fix-it-file, no
300                                 // auto-fixing)
301                                 Boolean finalShutdownFlag = true;
302                                 LOGGER.info(" Call doTheGrooming() ");
303                                 Boolean cacheDbOkFlag = true;
304                                 if( neverUseCache ){
305                                         // They have forbidden us from using a cached db connection.
306                                         cacheDbOkFlag = false;
307                                 }
308                                 doTheGrooming("", edgesOnlyFlag, dontFixOrphansFlag,
309                                                 maxRecordsToFix, groomOutFileName, ver, singleCommits,
310                                                 dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn, 
311                                                 finalShutdownFlag, cacheDbOkFlag, 
312                                                 skipEdgeCheckFlag, windowStartTime);
313                         }
314                 } catch (Exception ex) {
315                         LOGGER.error("Exception while grooming data", ex);
316                 }
317
318                 LOGGER.info(" Done! ");
319                 System.exit(0);
320
321         }// End of main()
322
323         /**
324          * Do the grooming.
325          *
326          * @param fileNameForFixing the file name for fixing
327          * @param edgesOnlyFlag the edges only flag
328          * @param dontFixOrphansFlag the dont fix orphans flag
329          * @param maxRecordsToFix the max records to fix
330          * @param groomOutFileName the groom out file name
331          * @param version the version
332          * @param singleCommits the single commits
333          * @param dupeCheckOff the dupe check off
334          * @param dupeFixOn the dupe fix on
335          * @param ghost2CheckOff the ghost 2 check off
336          * @param ghost2FixOn the ghost 2 fix on
337          * @param finalShutdownFlag the final shutdown flag
338          * @param cacheDbOkFlag the cacheDbOk flag
339          * @return the int
340          */
341         private static int doTheGrooming(String fileNameForFixing,
342                         Boolean edgesOnlyFlag, Boolean dontFixOrphansFlag,
343                         int maxRecordsToFix, String groomOutFileName, String version,
344                         Boolean singleCommits, 
345                         Boolean dupeCheckOff, Boolean dupeFixOn,
346                         Boolean ghost2CheckOff, Boolean ghost2FixOn, 
347                         Boolean finalShutdownFlag, Boolean cacheDbOkFlag,
348                         Boolean skipEdgeCheckFlag, long windowStartTime) {
349
350                 LOGGER.debug(" Entering doTheGrooming \n");
351
352                 int cleanupCandidateCount = 0;
353                 BufferedWriter bw = null;
354                 TitanGraph graph = null;
355                 TitanGraph graph2 = null;
356                 int deleteCount = 0;
357                 boolean executeFinalCommit = false;
358                 Set<String> deleteCandidateList = new LinkedHashSet<>();
359                 Set<String> processedVertices = new LinkedHashSet<>();
360                 Graph g = null;
361                 Graph g2 = null;
362                 try {
363                         AAIConfig.init();
364                         String targetDir = AAIConstants.AAI_HOME + AAIConstants.AAI_FILESEP
365                                         + "logs" + AAIConstants.AAI_FILESEP + "data"
366                                         + AAIConstants.AAI_FILESEP + "dataGrooming";
367
368                         // Make sure the target directory exists
369                         new File(targetDir).mkdirs();
370
371                         if (!fileNameForFixing.equals("")) {
372                                 deleteCandidateList = getDeleteList(targetDir,
373                                                 fileNameForFixing, edgesOnlyFlag, dontFixOrphansFlag,
374                                                 dupeFixOn);
375                         }
376
377                         if (deleteCandidateList.size() > maxRecordsToFix) {
378                                 LOGGER.warn(" >> WARNING >>  Delete candidate list size ("
379                                                 + deleteCandidateList.size()
380                                                 + ") is too big.  The maxFix we are using is: "
381                                                 + maxRecordsToFix
382                                                 + ".  No candidates will be deleted. ");
383                                 // Clear out the list so it won't be processed below.
384                                 deleteCandidateList = new LinkedHashSet<>();
385                         }
386
387                         String fullOutputFileName = targetDir + AAIConstants.AAI_FILESEP
388                                         + groomOutFileName;
389                         File groomOutFile = new File(fullOutputFileName);
390                         try {
391                                 groomOutFile.createNewFile();
392                         } catch (IOException e) {
393                                 String emsg = " Problem creating output file ["
394                                                 + fullOutputFileName + "], exception=" + e.getMessage();
395                                 throw new AAIException("AAI_6124", emsg);
396                         }
397
398                         LOGGER.info(" Will write to " + fullOutputFileName );
399                         bw = new BufferedWriter(new FileWriter(groomOutFile.getAbsoluteFile()));
400                         ErrorLogHelper.loadProperties();
401                         
402                         LOGGER.info("    ---- NOTE --- about to open graph (takes a little while)--------\n");
403
404                         if( cacheDbOkFlag ){
405                                 // Since we're just reading (not deleting/fixing anything), we can use 
406                                 // a cached connection to the DB
407                                 graph = TitanFactory.open(AAIConstants.CACHED_DB_CONFIG);
408                         }
409                         else {
410                                 graph = TitanFactory.open(AAIConstants.REALTIME_DB_CONFIG);
411                         }
412                         if (graph == null) {
413                                 String emsg = "null graph object in DataGrooming\n";
414                                 throw new AAIException("AAI_6101", emsg);
415                         }
416                 
417                         LOGGER.debug(" Got the graph object. ");
418                         
419                         g = graph.newTransaction();
420                         if (g == null) {
421                                 String emsg = "null graphTransaction object in DataGrooming\n";
422                                 throw new AAIException("AAI_6101", emsg);
423                         }
424                         GraphTraversalSource source1 = g.traversal();
425                         
426                         ArrayList<String> errArr = new ArrayList<>();
427                         int totalNodeCount = 0;
428                         HashMap<String, String> misMatchedHash = new HashMap<String, String>();
429                         HashMap<String, Vertex> orphanNodeHash = new HashMap<String, Vertex>();
430                         HashMap<String, Vertex> missingDepNodeHash = new HashMap<String, Vertex>();
431                         HashMap<String, Edge> oneArmedEdgeHash = new HashMap<String, Edge>();
432                         HashMap<String, String> emptyVertexHash = new HashMap<String, String>();
433                         HashMap<String, Vertex> ghostNodeHash = new HashMap<String, Vertex>();
434                         ArrayList<String> dupeGroups = new ArrayList<>();
435                         
436                         Loader loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
437
438                         Set<Entry<String, Introspector>> entrySet = loader.getAllObjects().entrySet();
439                         String ntList = "";
440
441                         LOGGER.info("  Starting DataGrooming Processing ");
442
443                         if (edgesOnlyFlag) {
444                                 LOGGER.info(" NOTE >> Skipping Node processing as requested.  Will only process Edges. << ");
445                         } 
446                         else {
447                                 for (Entry<String, Introspector> entry : entrySet) {
448                                         String nType = entry.getKey();
449                                         int thisNtCount = 0;
450                                         int thisNtDeleteCount = 0;
451                                         LOGGER.debug(" >  Look at : [" + nType + "] ...");
452                                         ntList = ntList + "," + nType;
453
454                                         // Get a collection of the names of the key properties for this nodeType to use later
455                                         // Determine what the key fields are for this nodeType
456                                         Collection <String> keyProps = entry.getValue().getKeys();
457                                         
458                                         // Get the types of nodes that this nodetype depends on for uniqueness (if any)
459                                         Collection <String> depNodeTypes = loader.introspectorFromName(nType).getDependentOn();
460                                         
461                                         // Loop through all the nodes of this Node type
462                                         int lastShownForNt = 0;
463                                         ArrayList <Vertex> tmpList = new ArrayList <> ();
464                                         Iterator <Vertex> iterv =  source1.V().has("aai-node-type",nType); 
465                                         while (iterv.hasNext()) {
466                                                 // We put the nodes into an ArrayList because the graph.query iterator can time out
467                                                 tmpList.add(iterv.next());
468                                         }
469                                         
470                                         Iterator <Vertex> iter = tmpList.iterator();
471                                         while (iter.hasNext()) {
472                                                 try {
473                                                         thisNtCount++;
474                                                         if( thisNtCount == lastShownForNt + 250 ){
475                                                                 lastShownForNt = thisNtCount;
476                                                                 LOGGER.debug("count for " + nType + " so far = " + thisNtCount );
477                                                         }
478                                                         Vertex thisVtx = iter.next();
479                                                         if( windowStartTime > 0 ){
480                                                                 // We only want nodes that are created after a passed-in timestamp
481                                                                 Object objTimeStamp = thisVtx.property("aai-created-ts").orElse(null);
482                                                                 if( objTimeStamp != null ){
483                                                                         long thisNodeCreateTime = (long)objTimeStamp;
484                                                                         if( thisNodeCreateTime < windowStartTime ){
485                                                                                 // It is NOT in our window, so we can pass over it
486                                                                                 continue;
487                                                                         }
488                                                                 }
489                                                         }
490                                                         
491                                                         String thisVid = thisVtx.id().toString();
492                                                         if (processedVertices.contains(thisVid)) {
493                                                                 LOGGER.debug("skipping already processed vertex: " + thisVid);
494                                                                 continue;
495                                                         }
496                                                         totalNodeCount++;
497                                                         List <Vertex> secondGetList = new ArrayList <> ();
498                                                         // -----------------------------------------------------------------------
499                                                         // For each vertex of this nodeType, we want to:
500                                                         //              a) make sure that it can be retrieved using it's AAI defined key
501                                                         //      b) make sure that it is not a duplicate
502                                                         // -----------------------------------------------------------------------
503                                                         
504                                                         // For this instance of this nodeType, get the key properties 
505                                                         HashMap<String, Object> propHashWithKeys = new HashMap<>();
506                                                         Iterator<String> keyPropI = keyProps.iterator();
507                                                         while (keyPropI.hasNext()) {
508                                                                 String propName = keyPropI.next();
509                                                                 String propVal = "";
510                                                                 //delete an already deleted vertex
511                                                                 Object obj = thisVtx.<Object>property(propName).orElse(null);
512                                                                 if (obj != null) {
513                                                                         propVal = obj.toString();
514                                                                 }
515                                                                 propHashWithKeys.put(propName, propVal);
516                                                         }
517                                                         try {
518                                                                 // If this node is dependent on another for uniqueness, then do the query from that parent node
519                                                                 // Note - all of our nodes that are dependent on others for uniqueness are 
520                                                                 //              "children" of that node.
521                                                                 boolean depNodeOk = true;
522                                                                 if( depNodeTypes.isEmpty() ){
523                                                                         // This kind of node is not dependent on any other.
524                                                                         // Make sure we can get it back using it's key properties and that we only get one.
525                                                                         secondGetList = getNodeJustUsingKeyParams( TRANSID, FROMAPPID, source1, nType, 
526                                                                                         propHashWithKeys, version );
527                                                                 } 
528                                                                 else {
529                                                                         // This kind of node is dependent on another for uniqueness.  
530                                                                         // Start at it's parent (the dependent vertex) and make sure we can get it
531                                                                         // back using it's key properties and that we only get one.
532                                                                         Iterator <Vertex> vertI2 = source1.V(thisVtx).union(__.inE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.OUT.toString()).outV(), __.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString()).inV());
533                                                                         Vertex parentVtx = null;
534                                                                         int pCount = 0;
535                                                                         while( vertI2 != null && vertI2.hasNext() ){
536                                                                                 parentVtx = vertI2.next();
537                                                                                 pCount++;
538                                                                         }
539                                                                         if( pCount <= 0 ){
540                                                                         
541                                                                         
542                                                                         //List<Vertex> vertI2 = g.traversal().V(thisVtx).union(__.outE().has("isParent-REV",true).outV(),__.inE().has("isParent",true).inV()).toList();
543                                                                         //if( vertI2.isEmpty()){
544                                                                                         
545                                                                                 // It's Missing it's dependent/parent node 
546                                                                                 depNodeOk = false;
547                                                                                 boolean zeroEdges = false;
548                                                                                 try {
549                                                                                         Iterator<Edge> tmpEdgeIter = thisVtx.edges(Direction.BOTH);
550                                                                                         int edgeCount = 0;
551                                                                                         while( tmpEdgeIter.hasNext() ){
552                                                                                                 edgeCount++;
553                                                                                                 tmpEdgeIter.next();
554                                                                                         }
555                                                                                         if( edgeCount == 0 ){  
556                                                                                                 zeroEdges = true;
557                                                                                         }
558                                                                                 } catch (Exception ex) {
559                                                                                         LOGGER.warn("WARNING from inside the for-each-vid-loop orphan-edges-check ", ex);
560                                                                                 }
561                                                                                 
562                                                                                 if (deleteCandidateList.contains(thisVid)) {
563                                                                                         boolean okFlag = true;
564                                                                                         try {
565                                                                                                 processedVertices.add(thisVtx.id().toString());
566                                                                                                 thisVtx.remove();
567                                                                                                 deleteCount++;
568                                                                                                 thisNtDeleteCount++;
569                                                                                         } catch (Exception e) {
570                                                                                                 okFlag = false;
571                                                                                                 LOGGER.error("ERROR trying to delete missing-dep-node VID = " + thisVid, e);
572                                                                                         }
573                                                                                         if (okFlag) {
574                                                                                                 LOGGER.info(" DELETED missing-dep-node VID = " + thisVid);
575                                                                                         }
576                                                                                 } else {
577                                                                                         // We count nodes missing their depNodes two ways - the first if it has
578                                                                                         //    at least some edges, and the second if it has zero edges.  Either
579                                                                                         //    way, they are effectively orphaned.
580                                                                                         // NOTE - Only nodes that have dependent nodes are ever considered "orphaned".
581                                                                                         if( zeroEdges ){
582                                                                                                 missingDepNodeHash.put(thisVid, thisVtx);
583                                                                                         }
584                                                                                         else {
585                                                                                                 orphanNodeHash.put(thisVid, thisVtx);
586                                                                                         }
587                                                                                 }
588                                                                         }
589                                                                         else if ( pCount > 1 ){
590                                                                                 // Not sure how this could happen?  Should we do something here?
591                                                                                 depNodeOk = false;
592                                                                         }
593                                                                         else {
594                                                                                 // We found the parent - so use it to do the second-look.
595                                                                                 // NOTE --- We're just going to do the same check from the other direction - because
596                                                                                 //  there could be duplicates or the pointer going the other way could be broken
597                                                                                 ArrayList <Vertex> tmpListSec = new ArrayList <> ();
598                                                                                 
599                                                                                 tmpListSec = getConnectedChildrenOfOneType( source1, parentVtx, nType ) ;
600                                                                                 Iterator<Vertex> vIter = tmpListSec.iterator();
601                                                                                 while (vIter.hasNext()) {
602                                                                                         Vertex tmpV = vIter.next();
603                                                                                         if( vertexHasTheseKeys(tmpV, propHashWithKeys) ){
604                                                                                                 secondGetList.add(tmpV);
605                                                                                         }
606                                                                                 }
607                                                                         }
608                                                                 }
609                                                                 
610                                                                 if( depNodeOk && (secondGetList == null || secondGetList.size() == 0) ){
611                                                                         // We could not get the node back using it's own key info. 
612                                                                         // So, it's a PHANTOM
613                                                                         if (deleteCandidateList.contains(thisVid)) {
614                                                                                 boolean okFlag = true;
615                                                                                 try {
616                                                                                         thisVtx.remove();
617                                                                                         deleteCount++;
618                                                                                         thisNtDeleteCount++;
619                                                                                 } catch (Exception e) {
620                                                                                         okFlag = false;
621                                                                                         LOGGER.error("ERROR trying to delete phantom VID = " + thisVid, e);
622                                                                                 }
623                                                                                 if (okFlag) {
624                                                                                         LOGGER.info(" DELETED VID = " + thisVid);
625                                                                                 }
626                                                                         } else {
627                                                                                 ghostNodeHash.put(thisVid, thisVtx);
628                                                                         }
629                                                                 }
630                                                                 else if( (secondGetList.size() > 1) && depNodeOk && !dupeCheckOff ){
631                                                                         // Found some DUPLICATES - need to process them
632                                                                         LOGGER.info(" - now check Dupes for this guy - ");
633                                                                         List<String> tmpDupeGroups = checkAndProcessDupes(
634                                                                                                 TRANSID, FROMAPPID, g, source1, version,
635                                                                                                 nType, secondGetList, dupeFixOn,
636                                                                                                 deleteCandidateList, singleCommits,     dupeGroups, loader);
637                                                                         Iterator<String> dIter = tmpDupeGroups.iterator();
638                                                                         while (dIter.hasNext()) {
639                                                                                 // Add in any newly found dupes to our running list
640                                                                                 String tmpGrp = dIter.next();
641                                                                                 LOGGER.info("Found set of dupes: [" + tmpGrp + "]");
642                                                                                 dupeGroups.add(tmpGrp);
643                                                                         }
644                                                                 }
645                                                         } 
646                                                         catch (AAIException e1) {
647                                                                 LOGGER.warn(" For nodeType = " + nType + " Caught exception", e1);
648                                                                 errArr.add(e1.getErrorObject().toString());
649                                                         }
650                                                         catch (Exception e2) {
651                                                                 LOGGER.warn(" For nodeType = " + nType
652                                                                                 + " Caught exception", e2);
653                                                                 errArr.add(e2.getMessage());
654                                                         }
655                                                 }// try block to enclose looping of a single vertex
656                                                 catch (Exception exx) {
657                                                         LOGGER.warn("WARNING from inside the while-verts-loop ", exx);
658                                                 }
659                                                 
660                                         } // while loop for each record of a nodeType
661                                         
662                                         if ( (thisNtDeleteCount > 0) && singleCommits ) {
663                                                 // NOTE - the singleCommits option is not used in normal processing
664                                                 g.tx().commit();
665                                                 g = AAIGraph.getInstance().getGraph().newTransaction();
666                                                 
667                                         }
668                                         thisNtDeleteCount = 0;
669                                         LOGGER.info( " Processed " + thisNtCount + " records for [" + nType + "], " + totalNodeCount + " total overall. " );
670                                         
671                                 }// While-loop for each node type
672                         }// end of check to make sure we weren't only supposed to do edges
673
674                 
675                   if( !skipEdgeCheckFlag ){
676                         // --------------------------------------------------------------------------------------
677                         // Now, we're going to look for one-armed-edges. Ie. an edge that
678                         // should have
679                         // been deleted (because a vertex on one side was deleted) but
680                         // somehow was not deleted.
681                         // So the one end of it points to a vertexId -- but that vertex is
682                         // empty.
683                         // --------------------------------------------------------------------------------------
684
685                         // To do some strange checking - we need a second graph object
686                         LOGGER.debug("    ---- DEBUG --- about to open a SECOND graph (takes a little while)--------\n");
687                         // Note - graph2 just reads - but we want it to use a fresh connection to 
688                         //      the database, so we are NOT using the CACHED DB CONFIG here.
689                         graph2 = TitanFactory.open(AAIConstants.REALTIME_DB_CONFIG);
690                         if (graph2 == null) {
691                                 String emsg = "null graph2 object in DataGrooming\n";
692                                 throw new AAIException("AAI_6101", emsg);
693                         } else {
694                                 LOGGER.debug("Got the graph2 object... \n");
695                         }
696                         g2 = graph2.newTransaction();
697                         if (g2 == null) {
698                                 String emsg = "null graphTransaction2 object in DataGrooming\n";
699                                 throw new AAIException("AAI_6101", emsg);
700                         }
701                         
702                         ArrayList<Vertex> vertList = new ArrayList<>();
703                         Iterator<Vertex> vItor3 = g.traversal().V();
704                         // Gotta hold these in a List - or else HBase times out as you cycle
705                         // through these
706                         while (vItor3.hasNext()) {
707                                 Vertex v = vItor3.next();
708                                 vertList.add(v);
709                         }
710                         int counter = 0;
711                         int lastShown = 0;
712                         Iterator<Vertex> vItor2 = vertList.iterator();
713                         LOGGER.info(" Checking for bad edges  --- ");
714
715                         while (vItor2.hasNext()) {
716                                 Vertex v = null;
717                                 try {
718                                         try {
719                                                 v = vItor2.next();
720                                         } catch (Exception vex) {
721                                                 LOGGER.warn(">>> WARNING trying to get next vertex on the vItor2 ");
722                                                 continue;
723                                         }
724                                         
725                                         counter++;
726                                         String thisVertId = "";
727                                         try {
728                                                 thisVertId = v.id().toString();
729                                         } catch (Exception ev) {
730                                                 LOGGER.warn("WARNING when doing getId() on a vertex from our vertex list.  ");
731                                                 continue;
732                                         }
733                                         if (ghostNodeHash.containsKey(thisVertId)) {
734                                                 // This is a phantom node, so don't try to use it
735                                                 LOGGER.info(" >> Skipping edge check for edges from vertexId = "
736                                                                                 + thisVertId
737                                                                                 + ", since that guy is a Phantom Node");
738                                                 continue;
739                                         }
740                                         
741                                         if( windowStartTime > 0 ){
742                                                 // We only want to look at nodes that are created after a passed-in timestamp
743                                                 Object objTimeStamp = v.property("aai-created-ts").orElse(null);
744                                                 if( objTimeStamp != null ){
745                                                         long thisNodeCreateTime = (long)objTimeStamp;
746                                                         if( thisNodeCreateTime < windowStartTime ){
747                                                                 // It is NOT in our window, so we can pass over it
748                                                                 continue;
749                                                         }
750                                                 }
751                                         }
752                                         
753                                         if (counter == lastShown + 250) {
754                                                 lastShown = counter;
755                                                 LOGGER.info("... Checking edges for vertex # "
756                                                                 + counter);
757                                         }
758                                         Iterator<Edge> eItor = v.edges(Direction.BOTH);
759                                         while (eItor.hasNext()) {
760                                                 Edge e = null;
761                                                 Vertex vIn = null;
762                                                 Vertex vOut = null;
763                                                 try {
764                                                         e = eItor.next();
765                                                 } catch (Exception iex) {
766                                                         LOGGER.warn(">>> WARNING trying to get next edge on the eItor ", iex);
767                                                         continue;
768                                                 }
769
770                                                 try {
771                                                         vIn = e.inVertex();
772                                                 } catch (Exception err) {
773                                                         LOGGER.warn(">>> WARNING trying to get edge's In-vertex ", err);
774                                                 }
775                                                 String vNtI = "";
776                                                 String vIdI = "";
777                                                 Vertex ghost2 = null;
778                                                 
779                                                 Boolean keysMissing = true;
780                                                 Boolean cantGetUsingVid = false;
781                                                 if (vIn != null) {
782                                                         try {
783                                                                 Object ob = vIn.<Object>property("aai-node-type").orElse(null);
784                                                                 if (ob != null) {
785                                                                         vNtI = ob.toString();
786                                                                         keysMissing = anyKeyFieldsMissing(vNtI, vIn, loader);
787                                                                 }
788                                                                 ob = vIn.id();
789                                                                 long vIdLong = 0L;
790                                                                 if (ob != null) {
791                                                                         vIdI = ob.toString();
792                                                                         vIdLong = Long.parseLong(vIdI);
793                                                                 }
794                                                                 
795                                                                 if( ! ghost2CheckOff ){
796                                                                         Vertex connectedVert = g2.traversal().V(vIdLong).next();
797                                                                         if( connectedVert == null ) {
798                                                                                 LOGGER.warn( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
799                                                                                 cantGetUsingVid = true;
800                                                                                 
801                                                                                 // If we can NOT get this ghost with the SECOND graph-object, 
802                                                                                 // it is still a ghost since even though we can get data about it using the FIRST graph 
803                                                                                 // object.  
804                                                                                 try {
805                                                                                          ghost2 = g.traversal().V(vIdLong).next();
806                                                                                 }
807                                                                                 catch( Exception ex){
808                                                                                         LOGGER.warn( "GHOST2 --  Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
809                                                                                 }
810                                                                                 if( ghost2 != null ){
811                                                                                         ghostNodeHash.put(vIdI, ghost2);
812                                                                                 }
813                                                                         }
814                                                                 }// end of the ghost2 checking
815                                                         } 
816                                                         catch (Exception err) {
817                                                                 LOGGER.warn(">>> WARNING trying to get edge's In-vertex props ", err);
818                                                         }
819                                                 }
820                                                 if (keysMissing || vIn == null || vNtI.equals("")
821                                                                 || cantGetUsingVid) {
822                                                         // this is a bad edge because it points to a vertex
823                                                         // that isn't there anymore or is corrupted
824                                                         String thisEid = e.id().toString();
825                                                         if (deleteCandidateList.contains(thisEid) || deleteCandidateList.contains(vIdI)) {
826                                                                 boolean okFlag = true;
827                                                                 if (!vIdI.equals("")) {
828                                                                         // try to get rid of the corrupted vertex
829                                                                         try {
830                                                                                 if( (ghost2 != null) && ghost2FixOn ){
831                                                                                         ghost2.remove();
832                                                                                 }
833                                                                                 else {
834                                                                                         vIn.remove();
835                                                                                 }
836                                                                                 if (singleCommits) {
837                                                                                         // NOTE - the singleCommits option is not used in normal processing
838                                                                                         g.tx().commit();
839                                                                                         g = AAIGraph.getInstance().getGraph().newTransaction();
840                                                                                 }
841                                                                                 deleteCount++;
842                                                                         } catch (Exception e1) {
843                                                                                 okFlag = false;
844                                                                                 LOGGER.warn("WARNING when trying to delete bad-edge-connected VERTEX VID = "
845                                                                                                 + vIdI, e1);
846                                                                         }
847                                                                         if (okFlag) {
848                                                                                 LOGGER.info(" DELETED vertex from bad edge = "
849                                                                                                                 + vIdI);
850                                                                         }
851                                                                 } else {
852                                                                         // remove the edge if we couldn't get the
853                                                                         // vertex
854                                                                         try {
855                                                                                 e.remove();
856                                                                                 if (singleCommits) {
857                                                                                         // NOTE - the singleCommits option is not used in normal processing
858                                                                                         g.tx().commit();
859                                                                                         g = AAIGraph.getInstance().getGraph().newTransaction();
860                                                                                 }
861                                                                                 deleteCount++;
862                                                                         } catch (Exception ex) {
863                                                                                 // NOTE - often, the exception is just
864                                                                                 // that this edge has already been
865                                                                                 // removed
866                                                                                 okFlag = false;
867                                                                                 LOGGER.warn("WARNING when trying to delete edge = "
868                                                                                                 + thisEid);
869                                                                         }
870                                                                         if (okFlag) {
871                                                                                 LOGGER.info(" DELETED edge = " + thisEid);
872                                                                         }
873                                                                 }
874                                                         } else {
875                                                                 oneArmedEdgeHash.put(thisEid, e);
876                                                                 if ((vIn != null) && (vIn.id() != null)) {
877                                                                         emptyVertexHash.put(thisEid, vIn.id()
878                                                                                         .toString());
879                                                                 }
880                                                         }
881                                                 }
882
883                                                 try {
884                                                         vOut = e.outVertex();
885                                                 } catch (Exception err) {
886                                                         LOGGER.warn(">>> WARNING trying to get edge's Out-vertex ");
887                                                 }
888                                                 String vNtO = "";
889                                                 String vIdO = "";
890                                                 ghost2 = null;
891                                                 keysMissing = true;
892                                                 cantGetUsingVid = false;
893                                                 if (vOut != null) {
894                                                         try {
895                                                                 Object ob = vOut.<Object>property("aai-node-type").orElse(null);
896                                                                 if (ob != null) {
897                                                                         vNtO = ob.toString();
898                                                                         keysMissing = anyKeyFieldsMissing(vNtO,
899                                                                                         vOut, loader);
900                                                                 }
901                                                                 ob = vOut.id();
902                                                                 long vIdLong = 0L;
903                                                                 if (ob != null) {
904                                                                         vIdO = ob.toString();
905                                                                         vIdLong = Long.parseLong(vIdO);
906                                                                 }
907                                                                 
908                                                                 if( ! ghost2CheckOff ){
909                                                                         Vertex connectedVert = g2.traversal().V(vIdLong).next();
910                                                                         if( connectedVert == null ) {
911                                                                                 cantGetUsingVid = true;
912                                                                                 LOGGER.info( "GHOST2 -- got NULL when doing getVertex for vid = " + vIdLong);
913                                                                                 // If we can get this ghost with the other graph-object, then get it -- it's still a ghost
914                                                                                 try {
915                                                                                          ghost2 = g.traversal().V(vIdLong).next();
916                                                                                 }
917                                                                                 catch( Exception ex){
918                                                                                         LOGGER.warn( "GHOST2 -- Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
919                                                                                 }
920                                                                                 if( ghost2 != null ){
921                                                                                         ghostNodeHash.put(vIdO, ghost2);
922                                                                                 }
923                                                                         }
924                                                                 }
925                                                         } catch (Exception err) {
926                                                                 LOGGER.warn(">>> WARNING trying to get edge's Out-vertex props ", err);
927                                                         }
928                                                 }
929                                                 if (keysMissing || vOut == null || vNtO.equals("")
930                                                                 || cantGetUsingVid) {
931                                                         // this is a bad edge because it points to a vertex
932                                                         // that isn't there anymore
933                                                         String thisEid = e.id().toString();
934                                                         if (deleteCandidateList.contains(thisEid) || deleteCandidateList.contains(vIdO)) {
935                                                                 boolean okFlag = true;
936                                                                 if (!vIdO.equals("")) {
937                                                                         // try to get rid of the corrupted vertex
938                                                                         try {
939                                                                                 if( (ghost2 != null) && ghost2FixOn ){
940                                                                                         ghost2.remove();
941                                                                                 }
942                                                                                 else if (vOut != null) {
943                                                                                         vOut.remove();
944                                                                                 }
945                                                                                 if (singleCommits) {
946                                                                                         // NOTE - the singleCommits option is not used in normal processing
947                                                                                         g.tx().commit();
948                                                                                         g = AAIGraph.getInstance().getGraph().newTransaction();
949                                                                                 }
950                                                                                 deleteCount++;
951                                                                         } catch (Exception e1) {
952                                                                                 okFlag = false;
953                                                                                 LOGGER.warn("WARNING when trying to delete bad-edge-connected VID = "
954                                                                                                 + vIdO, e1);
955                                                                         }
956                                                                         if (okFlag) {
957                                                                                 LOGGER.info(" DELETED vertex from bad edge = "
958                                                                                                                 + vIdO);
959                                                                         }
960                                                                 } else {
961                                                                         // remove the edge if we couldn't get the
962                                                                         // vertex
963                                                                         try {
964                                                                                 e.remove();
965                                                                                 if (singleCommits) {
966                                                                                         // NOTE - the singleCommits option is not used in normal processing
967                                                                                         g.tx().commit();
968                                                                                         g = AAIGraph.getInstance().getGraph().newTransaction();
969                                                                                 }
970                                                                                 deleteCount++;
971                                                                         } catch (Exception ex) {
972                                                                                 // NOTE - often, the exception is just
973                                                                                 // that this edge has already been
974                                                                                 // removed
975                                                                                 okFlag = false;
976                                                                                 LOGGER.warn("WARNING when trying to delete edge = "
977                                                                                                 + thisEid, ex);
978                                                                         }
979                                                                         if (okFlag) {
980                                                                                 LOGGER.info(" DELETED edge = " + thisEid);
981                                                                         }
982                                                                 }
983                                                         } else {
984                                                                 oneArmedEdgeHash.put(thisEid, e);
985                                                                 if ((vOut != null) && (vOut.id() != null)) {
986                                                                         emptyVertexHash.put(thisEid, vOut.id()
987                                                                                         .toString());
988                                                                 }
989                                                         }
990                                                 }
991                                         }// End of while-edges-loop
992                                 } catch (Exception exx) {
993                                         LOGGER.warn("WARNING from in the while-verts-loop ", exx);
994                                 }
995                         }// End of while-vertices-loop (the edge-checking)
996                   }     // end of -- if we're not skipping the edge-checking 
997                         
998
999                         deleteCount = deleteCount + dupeGrpsDeleted;
1000                         if (!singleCommits && deleteCount > 0) {
1001                                 try {
1002                                         LOGGER.info("About to do the commit for "
1003                                                         + deleteCount + " removes. ");
1004                                         executeFinalCommit = true;
1005                                         LOGGER.info("Commit was successful ");
1006                                 } catch (Exception excom) {
1007                                         LOGGER.error(" >>>> ERROR <<<<   Could not commit changes. ", excom);
1008                                         deleteCount = 0;
1009                                 }
1010                         }
1011
1012                         int ghostNodeCount = ghostNodeHash.size();
1013                         int orphanNodeCount = orphanNodeHash.size();
1014                         int missingDepNodeCount = missingDepNodeHash.size();
1015                         int oneArmedEdgeCount = oneArmedEdgeHash.size();
1016                         int dupeCount = dupeGroups.size();
1017
1018                         deleteCount = deleteCount + dupeGrpsDeleted;
1019
1020                         bw.write("\n\n ============ Summary ==============\n");
1021                         bw.write("Ran these nodeTypes: " + ntList + "\n\n");
1022                         bw.write("There were this many delete candidates from previous run =  "
1023                                         + deleteCandidateList.size() + "\n");
1024                         if (dontFixOrphansFlag) {
1025                                 bw.write(" Note - we are not counting orphan nodes since the -dontFixOrphans parameter was used. \n");
1026                         }
1027                         bw.write("Deleted this many delete candidates =  " + deleteCount
1028                                         + "\n");
1029                         bw.write("Total number of nodes looked at =  " + totalNodeCount
1030                                         + "\n");
1031                         bw.write("Ghost Nodes identified = " + ghostNodeCount + "\n");
1032                         bw.write("Orphan Nodes identified =  " + orphanNodeCount + "\n");
1033                         bw.write("Bad Edges identified =  " + oneArmedEdgeCount + "\n");
1034                         bw.write("Missing Dependent Edge (but not orphaned) node count = "
1035                                         + missingDepNodeCount + "\n");
1036                         bw.write("Duplicate Groups count =  " + dupeCount + "\n");
1037                         bw.write("MisMatching Label/aai-node-type count =  "
1038                                         + misMatchedHash.size() + "\n");
1039
1040                         bw.write("\n ------------- Delete Candidates ---------\n");
1041                         for (Map.Entry<String, Vertex> entry : ghostNodeHash
1042                                         .entrySet()) {
1043                                 String vid = entry.getKey();
1044                                 bw.write("DeleteCandidate: Phantom Vid = [" + vid + "]\n");
1045                                 cleanupCandidateCount++;
1046                         }
1047                         for (Map.Entry<String, Vertex> entry : orphanNodeHash
1048                                         .entrySet()) {
1049                                 String vid = entry.getKey();
1050                                 bw.write("DeleteCandidate: OrphanDepNode Vid = [" + vid + "]\n");
1051                                 if (!dontFixOrphansFlag) {
1052                                         cleanupCandidateCount++;
1053                                 }
1054                         }
1055                         for (Map.Entry<String, Edge> entry : oneArmedEdgeHash.entrySet()) {
1056                                 String eid = entry.getKey();
1057                                 bw.write("DeleteCandidate: Bad EDGE Edge-id = [" + eid + "]\n");
1058                                 cleanupCandidateCount++;
1059                         }
1060                         for (Map.Entry<String, Vertex> entry : missingDepNodeHash
1061                                         .entrySet()) {
1062                                 String vid = entry.getKey();
1063                                 bw.write("DeleteCandidate: (maybe) missingDepNode Vid = ["
1064                                                 + vid + "]\n");
1065                                 cleanupCandidateCount++;
1066                         }
1067                         bw.write("\n-- NOTE - To see DeleteCandidates for Duplicates, you need to look in the Duplicates Detail section below.\n");
1068
1069                         bw.write("\n ------------- GHOST NODES - detail ");
1070                         for (Map.Entry<String, Vertex> entry : ghostNodeHash
1071                                         .entrySet()) {
1072                                 try {
1073                                         String vid = entry.getKey();
1074                                         bw.write("\n ==> Phantom Vid = " + vid + "\n");
1075                                         ArrayList<String> retArr = showPropertiesForNode(
1076                                                         TRANSID, FROMAPPID, entry.getValue());
1077                                         for (String info : retArr) {
1078                                                 bw.write(info + "\n");
1079                                         }
1080         
1081                                         retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1082                                                         entry.getValue());
1083                                         for (String info : retArr) {
1084                                                 bw.write(info + "\n");
1085                                         }
1086                                 } catch (Exception dex) {
1087                                         LOGGER.error("error trying to print detail info for a ghost-node:  ", dex);
1088                                 }
1089                         }
1090
1091                         bw.write("\n ------------- Missing Dependent Edge ORPHAN NODES - detail: ");
1092                         for (Map.Entry<String, Vertex> entry : orphanNodeHash
1093                                         .entrySet()) {
1094                                 try {
1095                                         String vid = entry.getKey();
1096                                         bw.write("\n> Orphan Node Vid = " + vid + "\n");
1097                                         ArrayList<String> retArr = showPropertiesForNode(
1098                                                         TRANSID, FROMAPPID, entry.getValue());
1099                                         for (String info : retArr) {
1100                                                 bw.write(info + "\n");
1101                                         }
1102         
1103                                         retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1104                                                         entry.getValue());
1105                                         for (String info : retArr) {
1106                                                 bw.write(info + "\n");
1107                                         }
1108                                 } catch (Exception dex) {
1109                                         LOGGER.error("error trying to print detail info for a Orphan Node /missing dependent edge", dex);
1110                                 }
1111                         }
1112
1113                         bw.write("\n ------------- Missing Dependent Edge (but not orphan) NODES: ");
1114                         for (Map.Entry<String, Vertex> entry : missingDepNodeHash
1115                                         .entrySet()) {
1116                                 try {
1117                                         String vid = entry.getKey();
1118                                         bw.write("\n>  Missing edge to Dependent Node (but has edges) Vid = "
1119                                                         + vid + "\n");
1120                                         ArrayList<String> retArr = showPropertiesForNode(
1121                                                         TRANSID, FROMAPPID, entry.getValue());
1122                                         for (String info : retArr) {
1123                                                 bw.write(info + "\n");
1124                                         }
1125         
1126                                         retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1127                                                         entry.getValue());
1128                                         for (String info : retArr) {
1129                                                 bw.write(info + "\n");
1130                                         }
1131                                 } catch (Exception dex) {
1132                                         LOGGER.error("error trying to print detail info for a node missing its dependent edge but not an orphan", dex);
1133                                 }
1134                         }
1135
1136                         bw.write("\n ------------- EDGES pointing to empty/bad vertices: ");
1137                         for (Map.Entry<String, Edge> entry : oneArmedEdgeHash.entrySet()) {
1138                                 try {
1139                                         String eid = entry.getKey();
1140                                         Edge thisE = entry.getValue();
1141                                         String badVid = emptyVertexHash.get(eid);
1142                                         bw.write("\n>  Edge pointing to bad vertex (Vid = "
1143                                                         + badVid + ") EdgeId = " + eid + "\n");
1144                                         bw.write("Label: [" + thisE.label() + "]\n");
1145                                         Iterator<Property<Object>> pI = thisE.properties();
1146                                         while (pI.hasNext()) {
1147                                                 Property<Object> propKey = pI.next();
1148                                                 bw.write("Prop: [" + propKey + "], val = ["
1149                                                                 + propKey.value() + "]\n");
1150                                         }
1151                                 } catch (Exception pex) {
1152                                         LOGGER.error("error trying to print empty/bad vertex data: ", pex);
1153                                 }
1154                         }
1155
1156                         bw.write("\n ------------- Duplicates: ");
1157                         Iterator<String> dupeIter = dupeGroups.iterator();
1158                         int dupeSetCounter = 0;
1159                         while (dupeIter.hasNext()) {
1160                                 dupeSetCounter++;
1161                                 String dset = (String) dupeIter.next();
1162
1163                                 bw.write("\n --- Duplicate Group # " + dupeSetCounter
1164                                                 + " Detail -----------\n");
1165                                 try {
1166                                         // We expect each line to have at least two vid's, followed
1167                                         // by the preferred one to KEEP
1168                                         String[] dupeArr = dset.split("\\|");
1169                                         ArrayList<String> idArr = new ArrayList<>();
1170                                         int lastIndex = dupeArr.length - 1;
1171                                         for (int i = 0; i <= lastIndex; i++) {
1172                                                 if (i < lastIndex) {
1173                                                         // This is not the last entry, it is one of the
1174                                                         // dupes, so we want to show all its info
1175                                                         bw.write("    >> Duplicate Group # "
1176                                                                         + dupeSetCounter + "  Node # " + i
1177                                                                         + " ----\n");
1178                                                         String vidString = dupeArr[i];
1179                                                         idArr.add(vidString);
1180                                                         long longVertId = Long.parseLong(vidString);
1181                                                         Iterator<Vertex> vtxIterator = g.vertices(longVertId);
1182                                                         Vertex vtx = null;
1183                                                         if (vtxIterator.hasNext()) {
1184                                                                 vtx = vtxIterator.next();
1185                                                         }
1186                                                         ArrayList<String> retArr = showPropertiesForNode(TRANSID, FROMAPPID, vtx);
1187                                                         for (String info : retArr) {
1188                                                                 bw.write(info + "\n");
1189                                                         }
1190
1191                                                         retArr = showAllEdgesForNode(TRANSID,
1192                                                                         FROMAPPID, vtx);
1193                                                         for (String info : retArr) {
1194                                                                 bw.write(info + "\n");
1195                                                         }
1196                                                 } else {
1197                                                         // This is the last entry which should tell us if we
1198                                                         // have a preferred keeper
1199                                                         String prefString = dupeArr[i];
1200                                                         if (prefString.equals("KeepVid=UNDETERMINED")) {
1201                                                                 bw.write("\n For this group of duplicates, could not tell which one to keep.\n");
1202                                                                 bw.write(" >>> This group needs to be taken care of with a manual/forced-delete.\n");
1203                                                         } else {
1204                                                                 // If we know which to keep, then the prefString
1205                                                                 // should look like, "KeepVid=12345"
1206                                                                 String[] prefArr = prefString.split("=");
1207                                                                 if (prefArr.length != 2
1208                                                                                 || (!prefArr[0].equals("KeepVid"))) {
1209                                                                         throw new Exception("Bad format. Expecting KeepVid=999999");
1210                                                                 } else {
1211                                                                         String keepVidStr = prefArr[1];
1212                                                                         if (idArr.contains(keepVidStr)) {
1213                                                                                 bw.write("\n The vertex we want to KEEP has vertexId = "
1214                                                                                                 + keepVidStr);
1215                                                                                 bw.write("\n The others become delete candidates: \n");
1216                                                                                 idArr.remove(keepVidStr);
1217                                                                                 for (int x = 0; x < idArr.size(); x++) {
1218                                                                                         cleanupCandidateCount++;
1219                                                                                         bw.write("DeleteCandidate: Duplicate Vid = ["
1220                                                                                                         + idArr.get(x) + "]\n");
1221                                                                                 }
1222                                                                         } else {
1223                                                                                 throw new Exception("ERROR - Vertex Id to keep not found in list of dupes.  dset = ["
1224                                                                                                 + dset + "]");
1225                                                                         }
1226                                                                 }
1227                                                         }// else we know which one to keep
1228                                                 }// else last entry
1229                                         }// for each vertex in a group
1230                                 } catch (Exception dex) {
1231                                         LOGGER.error("error trying to print duplicate vertex data", dex);
1232                                 }
1233
1234                         }// while - work on each group of dupes
1235
1236                         bw.write("\n ------------- Mis-matched Label/aai-node-type Nodes: \n ");
1237                         for (Map.Entry<String, String> entry : misMatchedHash.entrySet()) {
1238                                 String msg = entry.getValue();
1239                                 bw.write("MixedMsg = " + msg + "\n");
1240                         }
1241
1242                         bw.write("\n ------------- Got these errors while processing: \n");
1243                         Iterator<String> errIter = errArr.iterator();
1244                         while (errIter.hasNext()) {
1245                                 String line = (String) errIter.next();
1246                                 bw.write(line + "\n");
1247                         }
1248
1249                         bw.close();
1250
1251                         LOGGER.info("\n ------------- Done doing all the checks ------------ ");
1252                         LOGGER.info("Output will be written to " + fullOutputFileName);
1253
1254                         if (cleanupCandidateCount > 0) {
1255                                 // Technically, this is not an error -- but we're throwing this
1256                                 // error so that hopefully a
1257                                 // monitoring system will pick it up and do something with it.
1258                                 throw new AAIException("AAI_6123", "See file: [" + fullOutputFileName
1259                                                 + "] and investigate delete candidates. ");
1260                         }
1261                 } catch (AAIException e) {
1262                         LOGGER.error("Caught AAIException while grooming data", e);
1263                         ErrorLogHelper.logException(e);
1264                 } catch (Exception ex) {
1265                         LOGGER.error("Caught exception while grooming data", ex);
1266                         ErrorLogHelper.logError("AAI_6128", ex.getMessage() + ", resolve and rerun dataGrooming");
1267                 } finally {
1268
1269                         if (bw != null) {
1270                                 try {
1271                                         bw.close();
1272                                 } catch (IOException iox) {
1273                                         LOGGER.warn("Got an IOException trying to close bufferedWriter() \n", iox);
1274                                 }
1275                         }
1276
1277                         if (g != null && g.tx().isOpen()) {
1278                                 // Any changes that worked correctly should have already done
1279                                 // their commits.
1280                                 try {
1281                                         if (executeFinalCommit) {
1282                                                 g.tx().commit();
1283                                         }
1284                                         g.tx().rollback();
1285                                 } catch (Exception ex) {
1286                                         // Don't throw anything because Titan sometimes is just saying that the graph is already closed
1287                                         LOGGER.warn("WARNING from final graphTransaction.rollback()", ex);
1288                                 }
1289                         }
1290                         
1291                         if (g2 != null && g2.tx().isOpen()) {
1292                                 // Any changes that worked correctly should have already done
1293                                 // their commits.
1294                                 try {
1295                                         g2.tx().rollback();
1296                                 } catch (Exception ex) {
1297                                         // Don't throw anything because Titan sometimes is just saying that the graph is already closed
1298                                         LOGGER.warn("WARNING from final graphTransaction2.rollback()", ex);
1299                                 }
1300                         }
1301                                 
1302                         if( finalShutdownFlag ){
1303                                 try {
1304                                         if( graph != null && graph.isOpen() ){
1305                                                 graph.tx().close();
1306                                                 graph.close();
1307                                         }
1308                                 } catch (Exception ex) {
1309                                         // Don't throw anything because Titan sometimes is just saying that the graph is already closed{
1310                                         LOGGER.warn("WARNING from final graph.shutdown()", ex);
1311                                 }
1312                                 
1313                                 try {
1314                                         if( graph2 != null && graph2.isOpen() ){
1315                                                 graph2.tx().close();
1316                                                 graph2.close();
1317                                         }
1318                                 } catch (Exception ex) {
1319                                         // Don't throw anything because Titan sometimes is just saying that the graph is already closed{
1320                                         LOGGER.warn("WARNING from final graph2.shutdown()", ex);
1321                                 }
1322                         }
1323                                 
1324                 }
1325
1326                 return cleanupCandidateCount;
1327
1328         }// end of doTheGrooming()
1329         
1330         
1331         /**
1332          * Vertex has these keys.
1333          *
1334          * @param tmpV the tmp V
1335          * @param propHashWithKeys the prop hash with keys
1336          * @return the boolean
1337          */
1338         private static Boolean vertexHasTheseKeys( Vertex tmpV, HashMap <String, Object> propHashWithKeys) {
1339                 Iterator <?> it = propHashWithKeys.entrySet().iterator();
1340                 while( it.hasNext() ){
1341                         String propName = "";
1342                         String propVal = "";
1343                         Map.Entry <?,?>propEntry = (Map.Entry<?,?>)it.next();
1344                         Object propNameObj = propEntry.getKey();
1345                         if( propNameObj != null ){
1346                                 propName = propNameObj.toString();
1347                         }
1348                         Object propValObj = propEntry.getValue();
1349                         if( propValObj != null ){
1350                                 propVal = propValObj.toString();
1351                         }
1352                         Object checkValObj = tmpV.<Object>property(propName).orElse(null);
1353                         if( checkValObj == null ) {
1354                                 return false;
1355                         }
1356                         else if( !propVal.equals(checkValObj.toString()) ){
1357                                 return false;
1358                         }
1359                 }
1360                 return true;
1361         }       
1362         
1363         
1364         /**
1365          * Any key fields missing.
1366          *
1367          * @param nType the n type
1368          * @param v the v
1369          * @return the boolean
1370          */
1371         private static Boolean anyKeyFieldsMissing(String nType, Vertex v, Loader loader) {
1372                 
1373                 try {
1374                         Introspector obj = null;
1375                         try {
1376                                 obj = loader.introspectorFromName(nType);
1377                         } catch (AAIUnknownObjectException e) {
1378                                 // They gave us a non-empty nodeType but our NodeKeyProps does
1379                                 //   not have data for it.  Since we do not know what the
1380                                 //   key params are for this type of node, we will just
1381                                 //   return "false".
1382                                 String emsg = " -- WARNING -- Unrecognized nodeType: [" + nType 
1383                                                 + "].  We cannot determine required keys for this nType. ";
1384                                 // NOTE - this will be caught below and a "false" returned
1385                                 throw new AAIException("AAI_6121", emsg);
1386                         }       
1387                         
1388                         // Determine what the key fields are for this nodeType
1389                         Collection <String> keyPropNamesColl = obj.getKeys();
1390                         Iterator<String> keyPropI = keyPropNamesColl.iterator();
1391                         while (keyPropI.hasNext()) {
1392                                 String propName = keyPropI.next();
1393                                 Object ob = v.<Object>property(propName).orElse(null);
1394                                 if (ob == null || ob.toString().equals("")) {
1395                                         // It is missing a key property
1396                                         return true;
1397                                 }
1398                         }
1399                 } catch (AAIException e) {
1400                         // Something was wrong -- but since we weren't able to check
1401                         // the keys, we will not declare that it is missing keys.
1402                         return false;
1403                 }
1404                 return false;
1405         }
1406         
1407
1408         /**
1409          * Gets the delete list.
1410          *
1411          * @param targetDir the target dir
1412          * @param fileName the file name
1413          * @param edgesOnlyFlag the edges only flag
1414          * @param dontFixOrphans the dont fix orphans
1415          * @param dupeFixOn the dupe fix on
1416          * @return the delete list
1417          * @throws AAIException the AAI exception
1418          */
1419         private static Set<String> getDeleteList(String targetDir,
1420                         String fileName, Boolean edgesOnlyFlag, Boolean dontFixOrphans,
1421                         Boolean dupeFixOn) throws AAIException {
1422
1423                 // Look in the file for lines formated like we expect - pull out any
1424                 // Vertex Id's to delete on this run
1425                 Set<String> delList = new LinkedHashSet<>();
1426                 String fullFileName = targetDir + AAIConstants.AAI_FILESEP + fileName;
1427
1428                 try(BufferedReader br = new BufferedReader(new FileReader(fullFileName))) {
1429                         String line = br.readLine();
1430                         while (line != null) {
1431                                 if (!"".equals(line) && line.startsWith("DeleteCandidate")) {
1432                                         if (edgesOnlyFlag && (!line.contains("Bad Edge"))) {
1433                                                 // We're not going to process edge guys
1434                                         } else if (dontFixOrphans && line.contains("Orphan")) {
1435                                                 // We're not going to process orphans
1436                                         } else if (!dupeFixOn && line.contains("Duplicate")) {
1437                                                 // We're not going to process Duplicates
1438                                         } else {
1439                                                 int begIndex = line.indexOf("id = ");
1440                                                 int endIndex = line.indexOf("]");
1441                                                 String vidVal = line.substring(begIndex + 6, endIndex);
1442                                                 delList.add(vidVal);
1443                                         }
1444                                 }
1445                                 line = br.readLine();
1446                         }
1447                         br.close();
1448                 } catch (IOException e) {
1449                         throw new AAIException("AAI_6124", e, "Could not open input-file [" + fullFileName
1450                                         + "], exception= " + e.getMessage());
1451                 }
1452
1453                 return delList;
1454
1455         }// end of getDeleteList
1456
1457         /**
1458          * Gets the preferred dupe.
1459          *
1460          * @param transId the trans id
1461          * @param fromAppId the from app id
1462          * @param g the g
1463          * @param dupeVertexList the dupe vertex list
1464          * @param ver the ver
1465          * @return Vertex
1466          * @throws AAIException the AAI exception
1467          */
1468         public static Vertex getPreferredDupe(String transId,
1469                         String fromAppId, GraphTraversalSource g,
1470                         ArrayList<Vertex> dupeVertexList, String ver, Loader loader)
1471                         throws AAIException {
1472
1473                 // This method assumes that it is being passed a List of vertex objects
1474                 // which
1475                 // violate our uniqueness constraints.
1476
1477                 Vertex nullVtx = null;
1478
1479                 if (dupeVertexList == null) {
1480                         return nullVtx;
1481                 }
1482                 int listSize = dupeVertexList.size();
1483                 if (listSize == 0) {
1484                         return nullVtx;
1485                 }
1486                 if (listSize == 1) {
1487                         return (dupeVertexList.get(0));
1488                 }
1489
1490                 Vertex vtxPreferred = null;
1491                 Vertex currentFaveVtx = dupeVertexList.get(0);
1492                 for (int i = 1; i < listSize; i++) {
1493                         Vertex vtxB = dupeVertexList.get(i);
1494                         vtxPreferred = pickOneOfTwoDupes(transId, fromAppId, g,
1495                                         currentFaveVtx, vtxB, ver, loader);
1496                         if (vtxPreferred == null) {
1497                                 // We couldn't choose one
1498                                 return nullVtx;
1499                         } else {
1500                                 currentFaveVtx = vtxPreferred;
1501                         }
1502                 }
1503
1504                 return (currentFaveVtx);
1505
1506         } // end of getPreferredDupe()
1507
1508         /**
1509          * Pick one of two dupes.
1510          *
1511          * @param transId the trans id
1512          * @param fromAppId the from app id
1513          * @param g the g
1514          * @param vtxA the vtx A
1515          * @param vtxB the vtx B
1516          * @param ver the ver
1517          * @return Vertex
1518          * @throws AAIException the AAI exception
1519          */
1520         public static Vertex pickOneOfTwoDupes(String transId,
1521                         String fromAppId, GraphTraversalSource g, Vertex vtxA,
1522                         Vertex vtxB, String ver, Loader loader) throws AAIException {
1523
1524                 Vertex nullVtx = null;
1525                 Vertex preferredVtx = null;
1526
1527                 Long vidA = new Long(vtxA.id().toString());
1528                 Long vidB = new Long(vtxB.id().toString());
1529
1530                 String vtxANodeType = "";
1531                 String vtxBNodeType = "";
1532                 Object objType = vtxA.<Object>property("aai-node-type").orElse(null);
1533                 if (objType != null) {
1534                         vtxANodeType = objType.toString();
1535                 }
1536                 objType = vtxB.<Object>property("aai-node-type").orElse(null);
1537                 if (objType != null) {
1538                         vtxBNodeType = objType.toString();
1539                 }
1540
1541                 if (vtxANodeType.equals("") || (!vtxANodeType.equals(vtxBNodeType))) {
1542                         // Either they're not really dupes or there's some bad data - so
1543                         // don't pick one
1544                         return nullVtx;
1545                 }
1546
1547                 // Check that node A and B both have the same key values (or else they
1548                 // are not dupes)
1549                 // (We'll check dep-node later)
1550                 // Determine what the key fields are for this nodeType
1551                 Collection <String> keyProps = new ArrayList <>();
1552                 try {
1553                         keyProps = loader.introspectorFromName(vtxANodeType).getKeys();
1554                 } catch (AAIUnknownObjectException e) {
1555                         LOGGER.warn("Required property not found", e);
1556                         throw new AAIException("AAI_6105", "Required Property name(s) not found for nodeType = " + vtxANodeType + ")");
1557                 }
1558                 
1559                 Iterator<String> keyPropI = keyProps.iterator();
1560                 while (keyPropI.hasNext()) {
1561                         String propName = keyPropI.next();
1562                         String vtxAKeyPropVal = "";
1563                         objType = vtxA.<Object>property(propName).orElse(null);
1564                         if (objType != null) {
1565                                 vtxAKeyPropVal = objType.toString();
1566                         }
1567                         String vtxBKeyPropVal = "";
1568                         objType = vtxB.<Object>property(propName).orElse(null);
1569                         if (objType != null) {
1570                                 vtxBKeyPropVal = objType.toString();
1571                         }
1572
1573                         if (vtxAKeyPropVal.equals("")
1574                                         || (!vtxAKeyPropVal.equals(vtxBKeyPropVal))) {
1575                                 // Either they're not really dupes or they are missing some key
1576                                 // data - so don't pick one
1577                                 return nullVtx;
1578                         }
1579                 }
1580
1581                 // Collect the vid's and aai-node-types of the vertices that each vertex
1582                 // (A and B) is connected to.
1583                 ArrayList<String> vtxIdsConn2A = new ArrayList<>();
1584                 ArrayList<String> vtxIdsConn2B = new ArrayList<>();
1585                 HashMap<String, String> nodeTypesConn2A = new HashMap<>();
1586                 HashMap<String, String> nodeTypesConn2B = new HashMap<>();
1587
1588                 ArrayList<Vertex> vertListA = getConnectedNodes( g, vtxA );
1589                 if (vertListA != null) {
1590                         Iterator<Vertex> iter = vertListA.iterator();
1591                         while (iter.hasNext()) {
1592                                 Vertex tvCon = iter.next();
1593                                 String conVid = tvCon.id().toString();
1594                                 String nt = "";
1595                                 objType = tvCon.<Object>property("aai-node-type").orElse(null);
1596                                 if (objType != null) {
1597                                         nt = objType.toString();
1598                                 }
1599                                 nodeTypesConn2A.put(nt, conVid);
1600                                 vtxIdsConn2A.add(conVid);
1601                         }
1602                 }
1603
1604                 ArrayList<Vertex> vertListB = getConnectedNodes( g, vtxB );
1605                 if (vertListB != null) {
1606                         Iterator<Vertex> iter = vertListB.iterator();
1607                         while (iter.hasNext()) {
1608                                 Vertex tvCon = iter.next();
1609                                 String conVid = tvCon.id().toString();
1610                                 String nt = "";
1611                                 objType = tvCon.<Object>property("aai-node-type").orElse(null);
1612                                 if (objType != null) {
1613                                         nt = objType.toString();
1614                                 }
1615                                 nodeTypesConn2B.put(nt, conVid);
1616                                 vtxIdsConn2B.add(conVid);
1617                         }
1618                 }
1619
1620                 // 1 - If this kind of node needs a dependent node for uniqueness, then
1621                 // verify that they both nodes
1622                 // point to the same dependent node (otherwise they're not really
1623                 // duplicates)
1624                 // Note - there are sometimes more than one dependent node type since
1625                 // one nodeType can be used in
1626                 // different ways. But for a particular node, it will only have one
1627                 // dependent node that it's
1628                 // connected to.
1629                 Collection <String> depNodeTypes = loader.introspectorFromName(vtxANodeType).getDependentOn();
1630                         
1631                 if (depNodeTypes.isEmpty()) {
1632                         // This kind of node is not dependent on any other. That is ok.
1633                 } else {
1634                         String depNodeVtxId4A = "";
1635                         String depNodeVtxId4B = "";
1636                         Iterator<String> iter = depNodeTypes.iterator();
1637                         while (iter.hasNext()) {
1638                                 String depNodeType = iter.next();
1639                                 if (nodeTypesConn2A.containsKey(depNodeType)) {
1640                                         // This is the dependent node type that vertex A is using
1641                                         depNodeVtxId4A = nodeTypesConn2A.get(depNodeType);
1642                                 }
1643                                 if (nodeTypesConn2B.containsKey(depNodeType)) {
1644                                         // This is the dependent node type that vertex B is using
1645                                         depNodeVtxId4B = nodeTypesConn2B.get(depNodeType);
1646                                 }
1647                         }
1648                         if (depNodeVtxId4A.equals("")
1649                                         || (!depNodeVtxId4A.equals(depNodeVtxId4B))) {
1650                                 // Either they're not really dupes or there's some bad data - so
1651                                 // don't pick either one
1652                                 return nullVtx;
1653                         }
1654                 }
1655
1656                 if (vtxIdsConn2A.size() == vtxIdsConn2B.size()) {
1657                         // 2 - If they both have edges to all the same vertices, then return
1658                         // the one with the lower vertexId.
1659                         boolean allTheSame = true;
1660                         Iterator<String> iter = vtxIdsConn2A.iterator();
1661                         while (iter.hasNext()) {
1662                                 String vtxIdConn2A = iter.next();
1663                                 if (!vtxIdsConn2B.contains(vtxIdConn2A)) {
1664                                         allTheSame = false;
1665                                         break;
1666                                 }
1667                         }
1668
1669                         if (allTheSame) {
1670                                 if (vidA < vidB) {
1671                                         preferredVtx = vtxA;
1672                                 } else {
1673                                         preferredVtx = vtxB;
1674                                 }
1675                         }
1676                 } else if (vtxIdsConn2A.size() > vtxIdsConn2B.size()) {
1677                         // 3 - VertexA is connected to more things than vtxB.
1678                         // We'll pick VtxA if its edges are a superset of vtxB's edges.
1679                         boolean missingOne = false;
1680                         Iterator<String> iter = vtxIdsConn2B.iterator();
1681                         while (iter.hasNext()) {
1682                                 String vtxIdConn2B = iter.next();
1683                                 if (!vtxIdsConn2A.contains(vtxIdConn2B)) {
1684                                         missingOne = true;
1685                                         break;
1686                                 }
1687                         }
1688                         if (!missingOne) {
1689                                 preferredVtx = vtxA;
1690                         }
1691                 } else if (vtxIdsConn2B.size() > vtxIdsConn2A.size()) {
1692                         // 4 - VertexB is connected to more things than vtxA.
1693                         // We'll pick VtxB if its edges are a superset of vtxA's edges.
1694                         boolean missingOne = false;
1695                         Iterator<String> iter = vtxIdsConn2A.iterator();
1696                         while (iter.hasNext()) {
1697                                 String vtxIdConn2A = iter.next();
1698                                 if (!vtxIdsConn2B.contains(vtxIdConn2A)) {
1699                                         missingOne = true;
1700                                         break;
1701                                 }
1702                         }
1703                         if (!missingOne) {
1704                                 preferredVtx = vtxB;
1705                         }
1706                 } else {
1707                         preferredVtx = nullVtx;
1708                 }
1709
1710                 return (preferredVtx);
1711
1712         } // end of pickOneOfTwoDupes()
1713
1714         /**
1715          * Check and process dupes.
1716          *
1717          * @param transId the trans id
1718          * @param fromAppId the from app id
1719          * @param g the g
1720          * @param version the version
1721          * @param nType the n type
1722          * @param passedVertList the passed vert list
1723          * @param dupeFixOn the dupe fix on
1724          * @param deleteCandidateList the delete candidate list
1725          * @param singleCommits the single commits
1726          * @param alreadyFoundDupeGroups the already found dupe groups
1727          * @param dbMaps the db maps
1728          * @return the array list
1729          */
1730         private static List<String> checkAndProcessDupes(String transId,
1731                         String fromAppId, Graph g, GraphTraversalSource source, String version, String nType,
1732                         List<Vertex> passedVertList, Boolean dupeFixOn,
1733                         Set<String> deleteCandidateList, Boolean singleCommits,
1734                         ArrayList<String> alreadyFoundDupeGroups, Loader loader ) {
1735                 
1736                 ArrayList<String> returnList = new ArrayList<>();
1737                 ArrayList<Vertex> checkVertList = new ArrayList<>();
1738                 ArrayList<String> alreadyFoundDupeVidArr = new ArrayList<>();
1739                 Boolean noFilterList = true;
1740                 Iterator<String> afItr = alreadyFoundDupeGroups.iterator();
1741                 while (afItr.hasNext()) {
1742                         String dupeGrpStr = afItr.next();
1743                         String[] dupeArr = dupeGrpStr.split("\\|");
1744                         int lastIndex = dupeArr.length - 1;
1745                         for (int i = 0; i < lastIndex; i++) {
1746                                 // Note: we don't want the last one...
1747                                 String vidString = dupeArr[i];
1748                                 alreadyFoundDupeVidArr.add(vidString);
1749                                 noFilterList = false;
1750                         }
1751                 }
1752
1753                 // For a given set of Nodes that were found with a set of KEY
1754                 // Parameters, (nodeType + key data) we will
1755                 // see if we find any duplicate nodes that need to be cleaned up. Note -
1756                 // it's legit to have more than one
1757                 // node with the same key data if the nodes depend on a parent for
1758                 // uniqueness -- as long as the two nodes
1759                 // don't hang off the same Parent.
1760                 // If we find duplicates, and we can figure out which of each set of
1761                 // duplicates is the one that we
1762                 // think should be preserved, we will record that. Whether we can tell
1763                 // which one should be
1764                 // preserved or not, we will return info about any sets of duplicates
1765                 // found.
1766                 //
1767                 // Each element in the returned arrayList might look like this:
1768                 // "1234|5678|keepVid=UNDETERMINED" (if there were 2 dupes, and we
1769                 // couldn't figure out which one to keep)
1770                 // or, "100017|200027|30037|keepVid=30037" (if there were 3 dupes and we
1771                 // thought the third one was the one that should survive)
1772
1773                 // Because of the way the calling code loops over stuff, we can get the
1774                 // same data multiple times - so we should
1775                 // not process any vertices that we've already seen.
1776
1777                 try {
1778                         Iterator<Vertex> pItr = passedVertList.iterator();
1779                         while (pItr.hasNext()) {
1780                                 Vertex tvx = pItr.next();
1781                                 String passedId = tvx.id().toString();
1782                                 if (noFilterList || !alreadyFoundDupeVidArr.contains(passedId)) {
1783                                         // We haven't seen this one before - so we should check it.
1784                                         checkVertList.add(tvx);
1785                                 }
1786                         }
1787
1788                         if (checkVertList.size() < 2) {
1789                                 // Nothing new to check.
1790                                 return returnList;
1791                         }
1792
1793                         if (loader.introspectorFromName(nType).isTopLevel()) {
1794                                 // If this was a node that does NOT depend on other nodes for
1795                                 // uniqueness, and we
1796                                 // found more than one node using its key -- record the found
1797                                 // vertices as duplicates.
1798                                 String dupesStr = "";
1799                                 for (int i = 0; i < checkVertList.size(); i++) {
1800                                         dupesStr = dupesStr
1801                                                         + ((checkVertList.get(i))).id()
1802                                                                         .toString() + "|";
1803                                 }
1804                                 if (dupesStr != "") {
1805                                         Vertex prefV = getPreferredDupe(transId, fromAppId,
1806                                                         source, checkVertList, version, loader);
1807                                         if (prefV == null) {
1808                                                 // We could not determine which duplicate to keep
1809                                                 dupesStr = dupesStr + "KeepVid=UNDETERMINED";
1810                                                 returnList.add(dupesStr);
1811                                         } else {
1812                                                 dupesStr = dupesStr + "KeepVid=" + prefV.id();
1813                                                 Boolean didRemove = false;
1814                                                 if (dupeFixOn) {
1815                                                         didRemove = deleteNonKeepersIfAppropriate(g,
1816                                                                         dupesStr, prefV.id().toString(),
1817                                                                         deleteCandidateList, singleCommits);
1818                                                 }
1819                                                 if (didRemove) {
1820                                                         dupeGrpsDeleted++;
1821                                                 } else {
1822                                                         // keep them on our list
1823                                                         returnList.add(dupesStr);
1824                                                 }
1825                                         }
1826                                 }
1827                         } else {
1828                                 // More than one node have the same key fields since they may
1829                                 // depend on a parent node for uniqueness. Since we're finding 
1830                                 // more than one, we want to check to see if any of the
1831                                 // vertices that have this set of keys (and are the same nodeType)
1832                                 // are also pointing at the same 'parent' node.
1833                                 // Note: for a given set of key data, it is possible that there
1834                                 // could be more than one set of duplicates.
1835                                 HashMap<String, ArrayList<Vertex>> vertsGroupedByParentHash = groupVertsByDepNodes(
1836                                                 transId, fromAppId, source, version, nType,
1837                                                 checkVertList, loader);
1838                                 for (Map.Entry<String, ArrayList<Vertex>> entry : vertsGroupedByParentHash
1839                                                 .entrySet()) {
1840                                         ArrayList<Vertex> thisParentsVertList = entry
1841                                                         .getValue();
1842                                         if (thisParentsVertList.size() > 1) {
1843                                                 // More than one vertex found with the same key info
1844                                                 // hanging off the same parent/dependent node
1845                                                 String dupesStr = "";
1846                                                 for (int i = 0; i < thisParentsVertList.size(); i++) {
1847                                                         dupesStr = dupesStr
1848                                                                         + ((thisParentsVertList
1849                                                                                         .get(i))).id() + "|";
1850                                                 }
1851                                                 if (dupesStr != "") {
1852                                                         Vertex prefV = getPreferredDupe(transId,
1853                                                                         fromAppId, source, thisParentsVertList,
1854                                                                         version, loader);
1855
1856                                                         if (prefV == null) {
1857                                                                 // We could not determine which duplicate to
1858                                                                 // keep
1859                                                                 dupesStr = dupesStr + "KeepVid=UNDETERMINED";
1860                                                                 returnList.add(dupesStr);
1861                                                         } else {
1862                                                                 Boolean didRemove = false;
1863                                                                 dupesStr = dupesStr + "KeepVid="
1864                                                                                 + prefV.id().toString();
1865                                                                 if (dupeFixOn) {
1866                                                                         didRemove = deleteNonKeepersIfAppropriate(
1867                                                                                         g, dupesStr, prefV.id()
1868                                                                                                         .toString(),
1869                                                                                         deleteCandidateList, singleCommits);
1870                                                                 }
1871                                                                 if (didRemove) {
1872                                                                         dupeGrpsDeleted++;
1873                                                                 } else {
1874                                                                         // keep them on our list
1875                                                                         returnList.add(dupesStr);
1876                                                                 }
1877                                                         }
1878                                                 }
1879                                         }
1880                                 }
1881                         }
1882                 } catch (Exception e) {
1883                         LOGGER.warn(" >>> Threw an error in checkAndProcessDupes - just absorb this error and move on. ", e);
1884                 }
1885
1886                 return returnList;
1887
1888         }// End of checkAndProcessDupes()
1889
1890         /**
1891          * Group verts by dep nodes.
1892          *
1893          * @param transId the trans id
1894          * @param fromAppId the from app id
1895          * @param g the g
1896          * @param version the version
1897          * @param nType the n type
1898          * @param passedVertList the passed vert list
1899          * @param dbMaps the db maps
1900          * @return the hash map
1901          * @throws AAIException the AAI exception
1902          */
1903         private static HashMap<String, ArrayList<Vertex>> groupVertsByDepNodes(
1904                         String transId, String fromAppId, GraphTraversalSource g, String version,
1905                         String nType, ArrayList<Vertex> passedVertList, Loader loader)
1906                         throws AAIException {
1907                 // Given a list of Titan Vertices of one nodeType (see AAI-8956), group 
1908                 // them together by the parent node they depend on.
1909                 // Ie. if given a list of ip address nodes (assumed to all have the
1910                 // same key info) they might sit under several different parent vertices.
1911                 // Under Normal conditions, there would only be one per parent -- but
1912                 // we're trying to find duplicates - so we
1913                 // allow for the case where more than one is under the same parent node.
1914
1915                 HashMap<String, ArrayList<Vertex>> retHash = new HashMap<String, ArrayList<Vertex>>();
1916                 if (loader.introspectorFromName(nType).isTopLevel()) {
1917                         // This method really should not have been called if this is not the
1918                         // kind of node
1919                         // that depends on a parent for uniqueness, so just return the empty
1920                         // hash.
1921                         return retHash;
1922                 }
1923
1924                 // Find out what types of nodes the passed in nodes can depend on
1925                 ArrayList<String> depNodeTypeL = new ArrayList<>();
1926                 Collection<String> depNTColl = loader.introspectorFromName(nType).getDependentOn();
1927                 Iterator<String> ntItr = depNTColl.iterator();
1928                 while (ntItr.hasNext()) {
1929                         depNodeTypeL.add(ntItr.next());
1930                 }
1931                 // For each vertex, we want find its depended-on/parent vertex so we
1932                 // can track what other vertexes that are dependent on that same guy.
1933                 if (passedVertList != null) {
1934                         Iterator<Vertex> iter = passedVertList.iterator();
1935                         while (iter.hasNext()) {
1936                                 Vertex thisVert = iter.next();
1937                                 Vertex tmpParentVtx = getConnectedParent( g, thisVert );
1938                                 if( tmpParentVtx != null ) {
1939                                         String parentNt = null;
1940                                         Object obj = tmpParentVtx.<Object>property("aai-node-type").orElse(null);
1941                                         if (obj != null) {
1942                                                 parentNt = obj.toString();
1943                                         }
1944                                         if (depNTColl.contains(parentNt)) {
1945                                                 // This must be the parent/dependent node
1946                                                 String parentVid = tmpParentVtx.id().toString();
1947                                                 if (retHash.containsKey(parentVid)) {
1948                                                         // add this vert to the list for this parent key
1949                                                         retHash.get(parentVid).add(thisVert);
1950                                                 } else {
1951                                                         // This is the first one we found on this parent
1952                                                         ArrayList<Vertex> vList = new ArrayList<>();
1953                                                         vList.add(thisVert);
1954                                                         retHash.put(parentVid, vList);
1955                                                 }
1956                                         }
1957                                 }
1958                         }
1959                 }
1960
1961                 return retHash;
1962
1963         }// end of groupVertsByDepNodes()
1964
1965         /**
1966          * Delete non keepers if appropriate.
1967          *
1968          * @param g the g
1969          * @param dupeInfoString the dupe info string
1970          * @param vidToKeep the vid to keep
1971          * @param deleteCandidateList the delete candidate list
1972          * @param singleCommits the single commits
1973          * @return the boolean
1974          */
1975         private static Boolean deleteNonKeepersIfAppropriate(Graph g,
1976                         String dupeInfoString, String vidToKeep,
1977                         Set<String> deleteCandidateList, Boolean singleCommits) {
1978
1979                 Boolean deletedSomething = false;
1980                 // This assumes that the dupeInfoString is in the format of
1981                 // pipe-delimited vid's followed by
1982                 // ie. "3456|9880|keepVid=3456"
1983                 if (deleteCandidateList == null || deleteCandidateList.size() == 0) {
1984                         // No vid's on the candidate list -- so no deleting will happen on
1985                         // this run
1986                         return false;
1987                 }
1988
1989                 String[] dupeArr = dupeInfoString.split("\\|");
1990                 ArrayList<String> idArr = new ArrayList<>();
1991                 int lastIndex = dupeArr.length - 1;
1992                 for (int i = 0; i <= lastIndex; i++) {
1993                         if (i < lastIndex) {
1994                                 // This is not the last entry, it is one of the dupes,
1995                                 String vidString = dupeArr[i];
1996                                 idArr.add(vidString);
1997                         } else {
1998                                 // This is the last entry which should tell us if we have a
1999                                 // preferred keeper
2000                                 String prefString = dupeArr[i];
2001                                 if (prefString.equals("KeepVid=UNDETERMINED")) {
2002                                         // They sent us a bad string -- nothing should be deleted if
2003                                         // no dupe could be tagged as preferred
2004                                         return false;
2005                                 } else {
2006                                         // If we know which to keep, then the prefString should look
2007                                         // like, "KeepVid=12345"
2008                                         String[] prefArr = prefString.split("=");
2009                                         if (prefArr.length != 2 || (!prefArr[0].equals("KeepVid"))) {
2010                                                 LOGGER.error("Bad format. Expecting KeepVid=999999");
2011                                                 return false;
2012                                         } else {
2013                                                 String keepVidStr = prefArr[1];
2014                                                 if (idArr.contains(keepVidStr)) {
2015                                                         idArr.remove(keepVidStr);
2016
2017                                                         // So now, the idArr should just contain the vid's
2018                                                         // that we want to remove.
2019                                                         for (int x = 0; x < idArr.size(); x++) {
2020                                                                 boolean okFlag = true;
2021                                                                 String thisVid = idArr.get(x);
2022                                                                 if (deleteCandidateList.contains(thisVid)) {
2023                                                                         // This vid is a valid delete candidate from
2024                                                                         // a prev. run, so we can remove it.
2025                                                                         try {
2026                                                                                 long longVertId = Long
2027                                                                                                 .parseLong(thisVid);
2028                                                                                 Vertex vtx = g
2029                                                                                                 .traversal().V(longVertId).next();
2030                                                                                 vtx.remove();
2031                                                                                 if (singleCommits) {
2032                                                                                         // NOTE - the singleCommits option is not used in normal processing
2033                                                                                         g.tx().commit();
2034                                                                                         g = AAIGraph.getInstance().getGraph().newTransaction();
2035                                                                                 }
2036                                                                         } catch (Exception e) {
2037                                                                                 okFlag = false;
2038                                                                                 LOGGER.error("ERROR trying to delete VID = " + thisVid, e);
2039                                                                         }
2040                                                                         if (okFlag) {
2041                                                                                 LOGGER.info(" DELETED VID = " + thisVid);
2042                                                                                 deletedSomething = true;
2043                                                                         }
2044                                                                 }
2045                                                         }
2046                                                 } else {
2047                                                         LOGGER.error("ERROR - Vertex Id to keep not found in list of dupes.  dupeInfoString = ["
2048                                                                         + dupeInfoString + "]");
2049                                                         return false;
2050                                                 }
2051                                         }
2052                                 }// else we know which one to keep
2053                         }// else last entry
2054                 }// for each vertex in a group
2055
2056                 return deletedSomething;
2057
2058         }// end of deleteNonKeepersIfAppropriate()
2059
2060         
2061         /**
2062          * Gets the node just using key params.
2063          *
2064          * @param transId the trans id
2065          * @param fromAppId the from app id
2066          * @param graph the graph
2067          * @param nodeType the node type
2068          * @param keyPropsHash the key props hash
2069          * @param apiVersion the api version
2070          * @return the node just using key params
2071          * @throws AAIException the AAI exception
2072          */
2073         public static List <Vertex> getNodeJustUsingKeyParams( String transId, String fromAppId, GraphTraversalSource graph, String nodeType,
2074                         HashMap<String,Object> keyPropsHash, String apiVersion )         throws AAIException{
2075                 
2076                 List <Vertex> retVertList = new ArrayList <> ();
2077                 
2078                 // We assume that all NodeTypes have at least one key-property defined.  
2079                 // Note - instead of key-properties (the primary key properties), a user could pass
2080                 //        alternate-key values if they are defined for the nodeType.
2081                 List<String> kName = new ArrayList<>();
2082                 List<Object> kVal = new ArrayList<>();
2083                 if( keyPropsHash == null || keyPropsHash.isEmpty() ) {
2084                         throw new AAIException("AAI_6120", " NO key properties passed for this getNodeJustUsingKeyParams() request.  NodeType = [" + nodeType + "]. "); 
2085                 }
2086                 
2087                 int i = -1;
2088                 for( Map.Entry<String, Object> entry : keyPropsHash.entrySet() ){
2089                         i++;
2090                         kName.add(i, entry.getKey());
2091                         kVal.add(i, entry.getValue());
2092                 }
2093                 int topPropIndex = i;
2094                 Vertex tiV = null;
2095                 String propsAndValuesForMsg = "";
2096                 Iterator <Vertex> verts = null;
2097
2098                 try { 
2099                         if( topPropIndex == 0 ){
2100                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ") ";
2101                                 verts= graph.V().has(kName.get(0),kVal.get(0)).has("aai-node-type",nodeType);   
2102                         }       
2103                         else if( topPropIndex == 1 ){
2104                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
2105                                                 + kName.get(1) + " = " + kVal.get(1) + ") ";
2106                                 verts =  graph.V().has(kName.get(0),kVal.get(0)).has(kName.get(1),kVal.get(1)).has("aai-node-type",nodeType);   
2107                         }                       
2108                         else if( topPropIndex == 2 ){
2109                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
2110                                                 + kName.get(1) + " = " + kVal.get(1) + ", " 
2111                                                 + kName.get(2) + " = " + kVal.get(2) +  ") ";
2112                                 verts= graph.V().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);
2113                         }       
2114                         else if( topPropIndex == 3 ){
2115                                 propsAndValuesForMsg = " (" + kName.get(0) + " = " + kVal.get(0) + ", " 
2116                                                 + kName.get(1) + " = " + kVal.get(1) + ", " 
2117                                                 + kName.get(2) + " = " + kVal.get(2) + ", " 
2118                                                 + kName.get(3) + " = " + kVal.get(3) +  ") ";
2119                                 verts= graph.V().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);
2120                         }                       
2121                         else {
2122                                 throw new AAIException("AAI_6114", " We only support 4 keys per nodeType for now \n"); 
2123                         }
2124                 }
2125                 catch( Exception ex ){
2126                         LOGGER.error( " ERROR trying to get node for: [" + propsAndValuesForMsg + "]", ex);
2127                 }
2128
2129                 if( verts != null ){
2130                         while( verts.hasNext() ){
2131                                 tiV = verts.next();
2132                                 retVertList.add(tiV);
2133                         }
2134                 }
2135                 
2136                 if( retVertList.size() == 0 ){
2137                         LOGGER.debug("DEBUG No node found for nodeType = [" + nodeType +
2138                                         "], propsAndVal = " + propsAndValuesForMsg );
2139                 }
2140                 
2141                 return retVertList;
2142                 
2143         }// End of getNodeJustUsingKeyParams() 
2144         
2145         /**
2146          * Show all edges for node.
2147          *
2148          * @param transId the trans id
2149          * @param fromAppId the from app id
2150          * @param tVert the t vert
2151          * @return the array list
2152          */
2153         private static ArrayList <String> showAllEdgesForNode( String transId, String fromAppId, Vertex tVert ){ 
2154
2155                 ArrayList <String> retArr = new ArrayList <> ();
2156                 Iterator <Edge> eI = tVert.edges(Direction.IN);
2157                 if( ! eI.hasNext() ){
2158                         retArr.add("No IN edges were found for this vertex. ");
2159                 }
2160                 while( eI.hasNext() ){
2161                         Edge ed = eI.next();
2162                         String lab = ed.label();
2163                         Vertex vtx;
2164                         if (tVert.equals(ed.inVertex())) {
2165                                 vtx = ed.outVertex();
2166                         } else {
2167                                 vtx = ed.inVertex();
2168                         }
2169                         if( vtx == null ){
2170                                 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
2171                         }
2172                         else {
2173                                 String nType = vtx.<String>property("aai-node-type").orElse(null);
2174                                 String vid = vtx.id().toString();
2175                                 retArr.add("Found an IN edge (" + lab + ") to this vertex from a [" + nType + "] node with VtxId = " + vid );
2176                                 
2177                         }
2178                 }
2179                 
2180                 eI = tVert.edges(Direction.OUT);
2181                 if( ! eI.hasNext() ){
2182                         retArr.add("No OUT edges were found for this vertex. ");
2183                 }
2184                 while( eI.hasNext() ){
2185                         Edge ed =  eI.next();
2186                         String lab = ed.label();
2187                         Vertex vtx;
2188                         if (tVert.equals(ed.inVertex())) {
2189                                 vtx = ed.outVertex();
2190                         } else {
2191                                 vtx = ed.inVertex();
2192                         }
2193                         if( vtx == null ){
2194                                 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
2195                         }
2196                         else {
2197                                 String nType = vtx.<String>property("aai-node-type").orElse(null);
2198                                 String vid = vtx.id().toString();
2199                                 retArr.add("Found an OUT edge (" + lab + ") from this vertex to a [" + nType + "] node with VtxId = " + vid );
2200                         }
2201                 }
2202                 return retArr;
2203         }
2204
2205         
2206         /**
2207          * Show properties for node.
2208          *
2209          * @param transId the trans id
2210          * @param fromAppId the from app id
2211          * @param tVert the t vert
2212          * @return the array list
2213          */
2214         private static ArrayList <String> showPropertiesForNode( String transId, String fromAppId, Vertex tVert ){ 
2215
2216                 ArrayList <String> retArr = new ArrayList <> ();
2217                 if( tVert == null ){
2218                         retArr.add("null Node object passed to showPropertiesForNode()\n");
2219                 }
2220                 else {
2221                         String nodeType = "";
2222                         Object ob = tVert.<Object>property("aai-node-type").orElse(null);
2223                         if( ob == null ){
2224                                 nodeType = "null";
2225                         }
2226                         else{
2227                                 nodeType = ob.toString();
2228                         }
2229                         
2230                         retArr.add(" AAINodeType/VtxID for this Node = [" + nodeType + "/" + tVert.id() + "]");
2231                         retArr.add(" Property Detail: ");
2232                         Iterator<VertexProperty<Object>> pI = tVert.properties();
2233                         while( pI.hasNext() ){
2234                                 VertexProperty<Object> tp = pI.next();
2235                                 Object val = tp.value();
2236                                 retArr.add("Prop: [" + tp.key() + "], val = [" + val + "] ");
2237                         }
2238                 }
2239                 return retArr;
2240         }
2241
2242         
2243         private static ArrayList <Vertex> getConnectedNodes(GraphTraversalSource g, Vertex startVtx ) 
2244                         throws AAIException {
2245         
2246                 ArrayList <Vertex> retArr = new ArrayList <> ();
2247                 if( startVtx == null ){
2248                         return retArr;
2249                 }
2250                 else {
2251                          GraphTraversal<Vertex, Vertex> modPipe = null;
2252                          modPipe = g.V(startVtx).both();
2253                          if( modPipe != null && modPipe.hasNext() ){
2254                                 while( modPipe.hasNext() ){
2255                                         Vertex conVert = modPipe.next();
2256                                         retArr.add(conVert);
2257                                 }
2258                         }
2259                 }
2260                 return retArr;
2261                 
2262         }// End of getConnectedNodes()
2263         
2264
2265         private static ArrayList <Vertex> getConnectedChildrenOfOneType( GraphTraversalSource g, 
2266                         Vertex startVtx, String childNType ) throws AAIException{
2267                 
2268                 ArrayList <Vertex> childList = new ArrayList <> ();
2269                 Iterator <Vertex> vertI =  g.V(startVtx).union(__.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.OUT.toString()).inV(), __.inE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString()).outV());
2270                 
2271                 Vertex tmpVtx = null;
2272                 while( vertI != null && vertI.hasNext() ){
2273                         tmpVtx = vertI.next();
2274                         Object ob = tmpVtx.<Object>property("aai-node-type").orElse(null);
2275                         if (ob != null) {
2276                                 String tmpNt = ob.toString();
2277                                 if( tmpNt.equals(childNType)){
2278                                         childList.add(tmpVtx);
2279                                 }
2280                         }
2281                 }
2282                 
2283                 return childList;               
2284
2285         }// End of getConnectedChildrenOfOneType()
2286
2287
2288         private static Vertex getConnectedParent( GraphTraversalSource g, 
2289                         Vertex startVtx ) throws AAIException{
2290                 
2291                 Vertex parentVtx = null;
2292                 Iterator <Vertex> vertI = g.V(startVtx).union(__.inE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.OUT.toString()).outV(), __.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString()).inV());
2293                                 
2294                 while( vertI != null && vertI.hasNext() ){
2295                         // Note - there better only be one!
2296                         parentVtx = vertI.next();
2297                 }
2298                 
2299                 return parentVtx;               
2300
2301         }// End of getConnectedParent()
2302         
2303         
2304         private static long figureWindowStartTime( int timeWindowMinutes ){
2305                 // Given a window size, calculate what the start-timestamp would be.
2306                 
2307                 if( timeWindowMinutes <= 0 ){
2308                         // This just means that there is no window...
2309                         return 0;
2310                 }
2311                 long unixTimeNow = System.currentTimeMillis();
2312                 long windowInMillis = timeWindowMinutes * 60L * 1000;
2313                 
2314                 long startTimeStamp = unixTimeNow - windowInMillis;
2315                 
2316                 return startTimeStamp;
2317         } // End of figureWindowStartTime()
2318         
2319         
2320 }