2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.dbgen;
24 import java.io.BufferedReader;
25 import java.io.BufferedWriter;
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;
37 import java.util.Map.Entry;
38 import java.util.Properties;
40 import java.util.UUID;
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;
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;
73 public class DataGrooming {
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;
83 * @param args the arguments
85 public static void main(String[] args) {
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;
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
109 int maxRecordsToFix = AAIConstants.AAI_GROOMING_DEFAULT_MAX_FIX;
110 int sleepMinutes = AAIConstants.AAI_GROOMING_DEFAULT_SLEEP_MINUTES;
112 String maxFixStr = AAIConfig.get("aai.grooming.default.max.fix");
113 if( maxFixStr != null && !maxFixStr.equals("") ){
114 maxRecordsToFix = Integer.parseInt(maxFixStr);
116 String sleepStr = AAIConfig.get("aai.grooming.default.sleep.minutes");
117 if( sleepStr != null && !sleepStr.equals("") ){
118 sleepMinutes = Integer.parseInt(sleepStr);
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. ");
126 String prevFileName = "";
128 FormatDate fd = new FormatDate("yyyyMMddHHmm", "GMT");
129 String dteStr = fd.getDateTime();
130 String groomOutFileName = "dataGrooming." + dteStr + ".out";
132 String argString = "";
133 for( int x = 0; x < args.length; x++ ) {
134 argString = argString + " " + args[x];
136 LOGGER.info(" DataGrooming called with these options: [" + argString + "]");
138 if (args.length > 0) {
139 // They passed some arguments in that will affect processing
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")) {
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")) {
155 } else if (thisArg.equals("-dupeFixOn")) {
157 } else if (thisArg.equals("-ghost2CheckOff")) {
158 ghost2CheckOff = true;
159 } else if (thisArg.equals("-neverUseCache")) {
160 neverUseCache = true;
161 } else if (thisArg.equals("-ghost2FixOn")) {
163 } else if (thisArg.equals("-skipEdgeChecks")) {
164 skipEdgeCheckFlag = true;
165 } else if (thisArg.equals("-maxFix")) {
167 if (i >= args.length) {
168 LOGGER.error(" No value passed with -maxFix option. ");
171 String nextArg = args[i];
173 maxRecordsToFix = Integer.parseInt(nextArg);
174 } catch (Exception e) {
175 LOGGER.error("Bad value passed with -maxFix option: ["
179 } else if (thisArg.equals("-sleepMinutes")) {
181 if (i >= args.length) {
182 LOGGER.error("No value passed with -sleepMinutes option.");
185 String nextArg = args[i];
187 sleepMinutes = Integer.parseInt(nextArg);
188 } catch (Exception e) {
189 LOGGER.error("Bad value passed with -sleepMinutes option: ["
193 } else if (thisArg.equals("-timeWindowMinutes")) {
195 if (i >= args.length) {
196 LOGGER.error("No value passed with -timeWindowMinutes option.");
199 String nextArg = args[i];
201 timeWindowMinutes = Integer.parseInt(nextArg);
202 } catch (Exception e) {
203 LOGGER.error("Bad value passed with -timeWindowMinutes option: ["
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 );
212 } else if (thisArg.equals("-f")) {
214 if (i >= args.length) {
215 LOGGER.error(" No value passed with -f option. ");
218 prevFileName = args[i];
220 LOGGER.error(" Unrecognized argument passed to DataGrooming: ["
222 LOGGER.error(" Valid values are: -f -autoFix -maxFix -edgesOnly -dupeFixOn -donFixOrphans -sleepMinutes -neverUseCache");
230 LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
233 catch (Exception ex){
234 LOGGER.error("ERROR - Could not create loader", ex);
240 if (!prevFileName.equals("")) {
241 // They are trying to fix some data based on a data in a
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
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. ");
271 // We'll sleep a little and then run a fix-pass based on the
272 // first-run's output file.
274 LOGGER.info("About to sleep for " + sleepMinutes
276 int sleepMsec = sleepMinutes * 60 * 1000;
277 Thread.sleep(sleepMsec);
278 } catch (InterruptedException ie) {
279 LOGGER.info("\n >>> Sleep Thread has been Interrupted <<< ");
283 dteStr = fd.getDateTime();
284 String secondGroomOutFileName = "dataGrooming." + dteStr
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);
299 // Do the grooming - plain vanilla (no fix-it-file, no
301 Boolean finalShutdownFlag = true;
302 LOGGER.info(" Call doTheGrooming() ");
303 Boolean cacheDbOkFlag = true;
305 // They have forbidden us from using a cached db connection.
306 cacheDbOkFlag = false;
308 doTheGrooming("", edgesOnlyFlag, dontFixOrphansFlag,
309 maxRecordsToFix, groomOutFileName, ver, singleCommits,
310 dupeCheckOff, dupeFixOn, ghost2CheckOff, ghost2FixOn,
311 finalShutdownFlag, cacheDbOkFlag,
312 skipEdgeCheckFlag, windowStartTime);
314 } catch (Exception ex) {
315 LOGGER.error("Exception while grooming data", ex);
318 LOGGER.info(" Done! ");
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
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) {
350 LOGGER.debug(" Entering doTheGrooming \n");
352 int cleanupCandidateCount = 0;
353 BufferedWriter bw = null;
354 TitanGraph graph = null;
355 TitanGraph graph2 = null;
357 boolean executeFinalCommit = false;
358 Set<String> deleteCandidateList = new LinkedHashSet<>();
359 Set<String> processedVertices = new LinkedHashSet<>();
364 String targetDir = AAIConstants.AAI_HOME + AAIConstants.AAI_FILESEP
365 + "logs" + AAIConstants.AAI_FILESEP + "data"
366 + AAIConstants.AAI_FILESEP + "dataGrooming";
368 // Make sure the target directory exists
369 new File(targetDir).mkdirs();
371 if (!fileNameForFixing.equals("")) {
372 deleteCandidateList = getDeleteList(targetDir,
373 fileNameForFixing, edgesOnlyFlag, dontFixOrphansFlag,
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: "
382 + ". No candidates will be deleted. ");
383 // Clear out the list so it won't be processed below.
384 deleteCandidateList = new LinkedHashSet<>();
387 String fullOutputFileName = targetDir + AAIConstants.AAI_FILESEP
389 File groomOutFile = new File(fullOutputFileName);
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);
398 LOGGER.info(" Will write to " + fullOutputFileName );
399 bw = new BufferedWriter(new FileWriter(groomOutFile.getAbsoluteFile()));
400 ErrorLogHelper.loadProperties();
402 LOGGER.info(" ---- NOTE --- about to open graph (takes a little while)--------\n");
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);
410 graph = TitanFactory.open(AAIConstants.REALTIME_DB_CONFIG);
413 String emsg = "null graph object in DataGrooming\n";
414 throw new AAIException("AAI_6101", emsg);
417 LOGGER.debug(" Got the graph object. ");
419 g = graph.newTransaction();
421 String emsg = "null graphTransaction object in DataGrooming\n";
422 throw new AAIException("AAI_6101", emsg);
424 GraphTraversalSource source1 = g.traversal();
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<>();
436 Loader loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
438 Set<Entry<String, Introspector>> entrySet = loader.getAllObjects().entrySet();
441 LOGGER.info(" Starting DataGrooming Processing ");
444 LOGGER.info(" NOTE >> Skipping Node processing as requested. Will only process Edges. << ");
447 for (Entry<String, Introspector> entry : entrySet) {
448 String nType = entry.getKey();
450 int thisNtDeleteCount = 0;
451 LOGGER.debug(" > Look at : [" + nType + "] ...");
452 ntList = ntList + "," + nType;
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();
458 // Get the types of nodes that this nodetype depends on for uniqueness (if any)
459 Collection <String> depNodeTypes = loader.introspectorFromName(nType).getDependentOn();
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());
470 Iterator <Vertex> iter = tmpList.iterator();
471 while (iter.hasNext()) {
474 if( thisNtCount == lastShownForNt + 250 ){
475 lastShownForNt = thisNtCount;
476 LOGGER.debug("count for " + nType + " so far = " + thisNtCount );
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
491 String thisVid = thisVtx.id().toString();
492 if (processedVertices.contains(thisVid)) {
493 LOGGER.debug("skipping already processed vertex: " + thisVid);
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 // -----------------------------------------------------------------------
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();
510 //delete an already deleted vertex
511 Object obj = thisVtx.<Object>property(propName).orElse(null);
513 propVal = obj.toString();
515 propHashWithKeys.put(propName, propVal);
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 );
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;
535 while( vertI2 != null && vertI2.hasNext() ){
536 parentVtx = vertI2.next();
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()){
545 // It's Missing it's dependent/parent node
547 boolean zeroEdges = false;
549 Iterator<Edge> tmpEdgeIter = thisVtx.edges(Direction.BOTH);
551 while( tmpEdgeIter.hasNext() ){
555 if( edgeCount == 0 ){
558 } catch (Exception ex) {
559 LOGGER.warn("WARNING from inside the for-each-vid-loop orphan-edges-check ", ex);
562 if (deleteCandidateList.contains(thisVid)) {
563 boolean okFlag = true;
565 processedVertices.add(thisVtx.id().toString());
569 } catch (Exception e) {
571 LOGGER.error("ERROR trying to delete missing-dep-node VID = " + thisVid, e);
574 LOGGER.info(" DELETED missing-dep-node VID = " + thisVid);
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".
582 missingDepNodeHash.put(thisVid, thisVtx);
585 orphanNodeHash.put(thisVid, thisVtx);
589 else if ( pCount > 1 ){
590 // Not sure how this could happen? Should we do something here?
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 <> ();
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);
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;
619 } catch (Exception e) {
621 LOGGER.error("ERROR trying to delete phantom VID = " + thisVid, e);
624 LOGGER.info(" DELETED VID = " + thisVid);
627 ghostNodeHash.put(thisVid, thisVtx);
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);
646 catch (AAIException e1) {
647 LOGGER.warn(" For nodeType = " + nType + " Caught exception", e1);
648 errArr.add(e1.getErrorObject().toString());
650 catch (Exception e2) {
651 LOGGER.warn(" For nodeType = " + nType
652 + " Caught exception", e2);
653 errArr.add(e2.getMessage());
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);
660 } // while loop for each record of a nodeType
662 if ( (thisNtDeleteCount > 0) && singleCommits ) {
663 // NOTE - the singleCommits option is not used in normal processing
665 g = AAIGraph.getInstance().getGraph().newTransaction();
668 thisNtDeleteCount = 0;
669 LOGGER.info( " Processed " + thisNtCount + " records for [" + nType + "], " + totalNodeCount + " total overall. " );
671 }// While-loop for each node type
672 }// end of check to make sure we weren't only supposed to do edges
675 if( !skipEdgeCheckFlag ){
676 // --------------------------------------------------------------------------------------
677 // Now, we're going to look for one-armed-edges. Ie. an edge that
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
683 // --------------------------------------------------------------------------------------
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);
694 LOGGER.debug("Got the graph2 object... \n");
696 g2 = graph2.newTransaction();
698 String emsg = "null graphTransaction2 object in DataGrooming\n";
699 throw new AAIException("AAI_6101", emsg);
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
706 while (vItor3.hasNext()) {
707 Vertex v = vItor3.next();
712 Iterator<Vertex> vItor2 = vertList.iterator();
713 LOGGER.info(" Checking for bad edges --- ");
715 while (vItor2.hasNext()) {
720 } catch (Exception vex) {
721 LOGGER.warn(">>> WARNING trying to get next vertex on the vItor2 ");
726 String thisVertId = "";
728 thisVertId = v.id().toString();
729 } catch (Exception ev) {
730 LOGGER.warn("WARNING when doing getId() on a vertex from our vertex list. ");
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 = "
737 + ", since that guy is a Phantom Node");
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
753 if (counter == lastShown + 250) {
755 LOGGER.info("... Checking edges for vertex # "
758 Iterator<Edge> eItor = v.edges(Direction.BOTH);
759 while (eItor.hasNext()) {
765 } catch (Exception iex) {
766 LOGGER.warn(">>> WARNING trying to get next edge on the eItor ", iex);
772 } catch (Exception err) {
773 LOGGER.warn(">>> WARNING trying to get edge's In-vertex ", err);
777 Vertex ghost2 = null;
779 Boolean keysMissing = true;
780 Boolean cantGetUsingVid = false;
783 Object ob = vIn.<Object>property("aai-node-type").orElse(null);
785 vNtI = ob.toString();
786 keysMissing = anyKeyFieldsMissing(vNtI, vIn, loader);
791 vIdI = ob.toString();
792 vIdLong = Long.parseLong(vIdI);
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;
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
805 ghost2 = g.traversal().V(vIdLong).next();
807 catch( Exception ex){
808 LOGGER.warn( "GHOST2 -- Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
810 if( ghost2 != null ){
811 ghostNodeHash.put(vIdI, ghost2);
814 }// end of the ghost2 checking
816 catch (Exception err) {
817 LOGGER.warn(">>> WARNING trying to get edge's In-vertex props ", err);
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
830 if( (ghost2 != null) && ghost2FixOn ){
837 // NOTE - the singleCommits option is not used in normal processing
839 g = AAIGraph.getInstance().getGraph().newTransaction();
842 } catch (Exception e1) {
844 LOGGER.warn("WARNING when trying to delete bad-edge-connected VERTEX VID = "
848 LOGGER.info(" DELETED vertex from bad edge = "
852 // remove the edge if we couldn't get the
857 // NOTE - the singleCommits option is not used in normal processing
859 g = AAIGraph.getInstance().getGraph().newTransaction();
862 } catch (Exception ex) {
863 // NOTE - often, the exception is just
864 // that this edge has already been
867 LOGGER.warn("WARNING when trying to delete edge = "
871 LOGGER.info(" DELETED edge = " + thisEid);
875 oneArmedEdgeHash.put(thisEid, e);
876 if ((vIn != null) && (vIn.id() != null)) {
877 emptyVertexHash.put(thisEid, vIn.id()
884 vOut = e.outVertex();
885 } catch (Exception err) {
886 LOGGER.warn(">>> WARNING trying to get edge's Out-vertex ");
892 cantGetUsingVid = false;
895 Object ob = vOut.<Object>property("aai-node-type").orElse(null);
897 vNtO = ob.toString();
898 keysMissing = anyKeyFieldsMissing(vNtO,
904 vIdO = ob.toString();
905 vIdLong = Long.parseLong(vIdO);
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
915 ghost2 = g.traversal().V(vIdLong).next();
917 catch( Exception ex){
918 LOGGER.warn( "GHOST2 -- Could not get the ghost info for a bad edge for vtxId = " + vIdLong, ex);
920 if( ghost2 != null ){
921 ghostNodeHash.put(vIdO, ghost2);
925 } catch (Exception err) {
926 LOGGER.warn(">>> WARNING trying to get edge's Out-vertex props ", err);
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
939 if( (ghost2 != null) && ghost2FixOn ){
942 else if (vOut != null) {
946 // NOTE - the singleCommits option is not used in normal processing
948 g = AAIGraph.getInstance().getGraph().newTransaction();
951 } catch (Exception e1) {
953 LOGGER.warn("WARNING when trying to delete bad-edge-connected VID = "
957 LOGGER.info(" DELETED vertex from bad edge = "
961 // remove the edge if we couldn't get the
966 // NOTE - the singleCommits option is not used in normal processing
968 g = AAIGraph.getInstance().getGraph().newTransaction();
971 } catch (Exception ex) {
972 // NOTE - often, the exception is just
973 // that this edge has already been
976 LOGGER.warn("WARNING when trying to delete edge = "
980 LOGGER.info(" DELETED edge = " + thisEid);
984 oneArmedEdgeHash.put(thisEid, e);
985 if ((vOut != null) && (vOut.id() != null)) {
986 emptyVertexHash.put(thisEid, vOut.id()
991 }// End of while-edges-loop
992 } catch (Exception exx) {
993 LOGGER.warn("WARNING from in the while-verts-loop ", exx);
995 }// End of while-vertices-loop (the edge-checking)
996 } // end of -- if we're not skipping the edge-checking
999 deleteCount = deleteCount + dupeGrpsDeleted;
1000 if (!singleCommits && deleteCount > 0) {
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);
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();
1018 deleteCount = deleteCount + dupeGrpsDeleted;
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");
1027 bw.write("Deleted this many delete candidates = " + deleteCount
1029 bw.write("Total number of nodes looked at = " + totalNodeCount
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");
1040 bw.write("\n ------------- Delete Candidates ---------\n");
1041 for (Map.Entry<String, Vertex> entry : ghostNodeHash
1043 String vid = entry.getKey();
1044 bw.write("DeleteCandidate: Phantom Vid = [" + vid + "]\n");
1045 cleanupCandidateCount++;
1047 for (Map.Entry<String, Vertex> entry : orphanNodeHash
1049 String vid = entry.getKey();
1050 bw.write("DeleteCandidate: OrphanDepNode Vid = [" + vid + "]\n");
1051 if (!dontFixOrphansFlag) {
1052 cleanupCandidateCount++;
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++;
1060 for (Map.Entry<String, Vertex> entry : missingDepNodeHash
1062 String vid = entry.getKey();
1063 bw.write("DeleteCandidate: (maybe) missingDepNode Vid = ["
1065 cleanupCandidateCount++;
1067 bw.write("\n-- NOTE - To see DeleteCandidates for Duplicates, you need to look in the Duplicates Detail section below.\n");
1069 bw.write("\n ------------- GHOST NODES - detail ");
1070 for (Map.Entry<String, Vertex> entry : ghostNodeHash
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");
1081 retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1083 for (String info : retArr) {
1084 bw.write(info + "\n");
1086 } catch (Exception dex) {
1087 LOGGER.error("error trying to print detail info for a ghost-node: ", dex);
1091 bw.write("\n ------------- Missing Dependent Edge ORPHAN NODES - detail: ");
1092 for (Map.Entry<String, Vertex> entry : orphanNodeHash
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");
1103 retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1105 for (String info : retArr) {
1106 bw.write(info + "\n");
1108 } catch (Exception dex) {
1109 LOGGER.error("error trying to print detail info for a Orphan Node /missing dependent edge", dex);
1113 bw.write("\n ------------- Missing Dependent Edge (but not orphan) NODES: ");
1114 for (Map.Entry<String, Vertex> entry : missingDepNodeHash
1117 String vid = entry.getKey();
1118 bw.write("\n> Missing edge to Dependent Node (but has edges) Vid = "
1120 ArrayList<String> retArr = showPropertiesForNode(
1121 TRANSID, FROMAPPID, entry.getValue());
1122 for (String info : retArr) {
1123 bw.write(info + "\n");
1126 retArr = showAllEdgesForNode(TRANSID, FROMAPPID,
1128 for (String info : retArr) {
1129 bw.write(info + "\n");
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);
1136 bw.write("\n ------------- EDGES pointing to empty/bad vertices: ");
1137 for (Map.Entry<String, Edge> entry : oneArmedEdgeHash.entrySet()) {
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");
1151 } catch (Exception pex) {
1152 LOGGER.error("error trying to print empty/bad vertex data: ", pex);
1156 bw.write("\n ------------- Duplicates: ");
1157 Iterator<String> dupeIter = dupeGroups.iterator();
1158 int dupeSetCounter = 0;
1159 while (dupeIter.hasNext()) {
1161 String dset = (String) dupeIter.next();
1163 bw.write("\n --- Duplicate Group # " + dupeSetCounter
1164 + " Detail -----------\n");
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
1178 String vidString = dupeArr[i];
1179 idArr.add(vidString);
1180 long longVertId = Long.parseLong(vidString);
1181 Iterator<Vertex> vtxIterator = g.vertices(longVertId);
1183 if (vtxIterator.hasNext()) {
1184 vtx = vtxIterator.next();
1186 ArrayList<String> retArr = showPropertiesForNode(TRANSID, FROMAPPID, vtx);
1187 for (String info : retArr) {
1188 bw.write(info + "\n");
1191 retArr = showAllEdgesForNode(TRANSID,
1193 for (String info : retArr) {
1194 bw.write(info + "\n");
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");
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");
1211 String keepVidStr = prefArr[1];
1212 if (idArr.contains(keepVidStr)) {
1213 bw.write("\n The vertex we want to KEEP has vertexId = "
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");
1223 throw new Exception("ERROR - Vertex Id to keep not found in list of dupes. dset = ["
1227 }// else we know which one to keep
1229 }// for each vertex in a group
1230 } catch (Exception dex) {
1231 LOGGER.error("error trying to print duplicate vertex data", dex);
1234 }// while - work on each group of dupes
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");
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");
1251 LOGGER.info("\n ------------- Done doing all the checks ------------ ");
1252 LOGGER.info("Output will be written to " + fullOutputFileName);
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. ");
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");
1272 } catch (IOException iox) {
1273 LOGGER.warn("Got an IOException trying to close bufferedWriter() \n", iox);
1277 if (g != null && g.tx().isOpen()) {
1278 // Any changes that worked correctly should have already done
1281 if (executeFinalCommit) {
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);
1291 if (g2 != null && g2.tx().isOpen()) {
1292 // Any changes that worked correctly should have already done
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);
1302 if( finalShutdownFlag ){
1304 if( graph != null && graph.isOpen() ){
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);
1314 if( graph2 != null && graph2.isOpen() ){
1315 graph2.tx().close();
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);
1326 return cleanupCandidateCount;
1328 }// end of doTheGrooming()
1332 * Vertex has these keys.
1334 * @param tmpV the tmp V
1335 * @param propHashWithKeys the prop hash with keys
1336 * @return the boolean
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();
1348 Object propValObj = propEntry.getValue();
1349 if( propValObj != null ){
1350 propVal = propValObj.toString();
1352 Object checkValObj = tmpV.<Object>property(propName).orElse(null);
1353 if( checkValObj == null ) {
1356 else if( !propVal.equals(checkValObj.toString()) ){
1365 * Any key fields missing.
1367 * @param nType the n type
1369 * @return the boolean
1371 private static Boolean anyKeyFieldsMissing(String nType, Vertex v, Loader loader) {
1374 Introspector obj = null;
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
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);
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
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.
1409 * Gets the delete list.
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
1419 private static Set<String> getDeleteList(String targetDir,
1420 String fileName, Boolean edgesOnlyFlag, Boolean dontFixOrphans,
1421 Boolean dupeFixOn) throws AAIException {
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;
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
1439 int begIndex = line.indexOf("id = ");
1440 int endIndex = line.indexOf("]");
1441 String vidVal = line.substring(begIndex + 6, endIndex);
1442 delList.add(vidVal);
1445 line = br.readLine();
1448 } catch (IOException e) {
1449 throw new AAIException("AAI_6124", e, "Could not open input-file [" + fullFileName
1450 + "], exception= " + e.getMessage());
1455 }// end of getDeleteList
1458 * Gets the preferred dupe.
1460 * @param transId the trans id
1461 * @param fromAppId the from app id
1463 * @param dupeVertexList the dupe vertex list
1464 * @param ver the ver
1466 * @throws AAIException the AAI exception
1468 public static Vertex getPreferredDupe(String transId,
1469 String fromAppId, GraphTraversalSource g,
1470 ArrayList<Vertex> dupeVertexList, String ver, Loader loader)
1471 throws AAIException {
1473 // This method assumes that it is being passed a List of vertex objects
1475 // violate our uniqueness constraints.
1477 Vertex nullVtx = null;
1479 if (dupeVertexList == null) {
1482 int listSize = dupeVertexList.size();
1483 if (listSize == 0) {
1486 if (listSize == 1) {
1487 return (dupeVertexList.get(0));
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
1500 currentFaveVtx = vtxPreferred;
1504 return (currentFaveVtx);
1506 } // end of getPreferredDupe()
1509 * Pick one of two dupes.
1511 * @param transId the trans id
1512 * @param fromAppId the from app id
1514 * @param vtxA the vtx A
1515 * @param vtxB the vtx B
1516 * @param ver the ver
1518 * @throws AAIException the AAI exception
1520 public static Vertex pickOneOfTwoDupes(String transId,
1521 String fromAppId, GraphTraversalSource g, Vertex vtxA,
1522 Vertex vtxB, String ver, Loader loader) throws AAIException {
1524 Vertex nullVtx = null;
1525 Vertex preferredVtx = null;
1527 Long vidA = new Long(vtxA.id().toString());
1528 Long vidB = new Long(vtxB.id().toString());
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();
1536 objType = vtxB.<Object>property("aai-node-type").orElse(null);
1537 if (objType != null) {
1538 vtxBNodeType = objType.toString();
1541 if (vtxANodeType.equals("") || (!vtxANodeType.equals(vtxBNodeType))) {
1542 // Either they're not really dupes or there's some bad data - so
1547 // Check that node A and B both have the same key values (or else they
1549 // (We'll check dep-node later)
1550 // Determine what the key fields are for this nodeType
1551 Collection <String> keyProps = new ArrayList <>();
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 + ")");
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();
1567 String vtxBKeyPropVal = "";
1568 objType = vtxB.<Object>property(propName).orElse(null);
1569 if (objType != null) {
1570 vtxBKeyPropVal = objType.toString();
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
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<>();
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();
1595 objType = tvCon.<Object>property("aai-node-type").orElse(null);
1596 if (objType != null) {
1597 nt = objType.toString();
1599 nodeTypesConn2A.put(nt, conVid);
1600 vtxIdsConn2A.add(conVid);
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();
1611 objType = tvCon.<Object>property("aai-node-type").orElse(null);
1612 if (objType != null) {
1613 nt = objType.toString();
1615 nodeTypesConn2B.put(nt, conVid);
1616 vtxIdsConn2B.add(conVid);
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
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
1629 Collection <String> depNodeTypes = loader.introspectorFromName(vtxANodeType).getDependentOn();
1631 if (depNodeTypes.isEmpty()) {
1632 // This kind of node is not dependent on any other. That is ok.
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);
1643 if (nodeTypesConn2B.containsKey(depNodeType)) {
1644 // This is the dependent node type that vertex B is using
1645 depNodeVtxId4B = nodeTypesConn2B.get(depNodeType);
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
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)) {
1671 preferredVtx = vtxA;
1673 preferredVtx = vtxB;
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)) {
1689 preferredVtx = vtxA;
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)) {
1704 preferredVtx = vtxB;
1707 preferredVtx = nullVtx;
1710 return (preferredVtx);
1712 } // end of pickOneOfTwoDupes()
1715 * Check and process dupes.
1717 * @param transId the trans id
1718 * @param fromAppId the from app id
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
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 ) {
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;
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
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)
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.
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);
1788 if (checkVertList.size() < 2) {
1789 // Nothing new to check.
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++) {
1801 + ((checkVertList.get(i))).id()
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);
1812 dupesStr = dupesStr + "KeepVid=" + prefV.id();
1813 Boolean didRemove = false;
1815 didRemove = deleteNonKeepersIfAppropriate(g,
1816 dupesStr, prefV.id().toString(),
1817 deleteCandidateList, singleCommits);
1822 // keep them on our list
1823 returnList.add(dupesStr);
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
1840 ArrayList<Vertex> thisParentsVertList = entry
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++) {
1848 + ((thisParentsVertList
1849 .get(i))).id() + "|";
1851 if (dupesStr != "") {
1852 Vertex prefV = getPreferredDupe(transId,
1853 fromAppId, source, thisParentsVertList,
1856 if (prefV == null) {
1857 // We could not determine which duplicate to
1859 dupesStr = dupesStr + "KeepVid=UNDETERMINED";
1860 returnList.add(dupesStr);
1862 Boolean didRemove = false;
1863 dupesStr = dupesStr + "KeepVid="
1864 + prefV.id().toString();
1866 didRemove = deleteNonKeepersIfAppropriate(
1867 g, dupesStr, prefV.id()
1869 deleteCandidateList, singleCommits);
1874 // keep them on our list
1875 returnList.add(dupesStr);
1882 } catch (Exception e) {
1883 LOGGER.warn(" >>> Threw an error in checkAndProcessDupes - just absorb this error and move on. ", e);
1888 }// End of checkAndProcessDupes()
1891 * Group verts by dep nodes.
1893 * @param transId the trans id
1894 * @param fromAppId the from app id
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
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.
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
1919 // that depends on a parent for uniqueness, so just return the empty
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());
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);
1942 parentNt = obj.toString();
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);
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);
1963 }// end of groupVertsByDepNodes()
1966 * Delete non keepers if appropriate.
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
1975 private static Boolean deleteNonKeepersIfAppropriate(Graph g,
1976 String dupeInfoString, String vidToKeep,
1977 Set<String> deleteCandidateList, Boolean singleCommits) {
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
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);
1998 // This is the last entry which should tell us if we have a
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
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");
2013 String keepVidStr = prefArr[1];
2014 if (idArr.contains(keepVidStr)) {
2015 idArr.remove(keepVidStr);
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.
2026 long longVertId = Long
2027 .parseLong(thisVid);
2029 .traversal().V(longVertId).next();
2031 if (singleCommits) {
2032 // NOTE - the singleCommits option is not used in normal processing
2034 g = AAIGraph.getInstance().getGraph().newTransaction();
2036 } catch (Exception e) {
2038 LOGGER.error("ERROR trying to delete VID = " + thisVid, e);
2041 LOGGER.info(" DELETED VID = " + thisVid);
2042 deletedSomething = true;
2047 LOGGER.error("ERROR - Vertex Id to keep not found in list of dupes. dupeInfoString = ["
2048 + dupeInfoString + "]");
2052 }// else we know which one to keep
2054 }// for each vertex in a group
2056 return deletedSomething;
2058 }// end of deleteNonKeepersIfAppropriate()
2062 * Gets the node just using key params.
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
2073 public static List <Vertex> getNodeJustUsingKeyParams( String transId, String fromAppId, GraphTraversalSource graph, String nodeType,
2074 HashMap<String,Object> keyPropsHash, String apiVersion ) throws AAIException{
2076 List <Vertex> retVertList = new ArrayList <> ();
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 + "]. ");
2088 for( Map.Entry<String, Object> entry : keyPropsHash.entrySet() ){
2090 kName.add(i, entry.getKey());
2091 kVal.add(i, entry.getValue());
2093 int topPropIndex = i;
2095 String propsAndValuesForMsg = "";
2096 Iterator <Vertex> verts = null;
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);
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);
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);
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);
2122 throw new AAIException("AAI_6114", " We only support 4 keys per nodeType for now \n");
2125 catch( Exception ex ){
2126 LOGGER.error( " ERROR trying to get node for: [" + propsAndValuesForMsg + "]", ex);
2129 if( verts != null ){
2130 while( verts.hasNext() ){
2132 retVertList.add(tiV);
2136 if( retVertList.size() == 0 ){
2137 LOGGER.debug("DEBUG No node found for nodeType = [" + nodeType +
2138 "], propsAndVal = " + propsAndValuesForMsg );
2143 }// End of getNodeJustUsingKeyParams()
2146 * Show all edges for node.
2148 * @param transId the trans id
2149 * @param fromAppId the from app id
2150 * @param tVert the t vert
2151 * @return the array list
2153 private static ArrayList <String> showAllEdgesForNode( String transId, String fromAppId, Vertex tVert ){
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. ");
2160 while( eI.hasNext() ){
2161 Edge ed = eI.next();
2162 String lab = ed.label();
2164 if (tVert.equals(ed.inVertex())) {
2165 vtx = ed.outVertex();
2167 vtx = ed.inVertex();
2170 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
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 );
2180 eI = tVert.edges(Direction.OUT);
2181 if( ! eI.hasNext() ){
2182 retArr.add("No OUT edges were found for this vertex. ");
2184 while( eI.hasNext() ){
2185 Edge ed = eI.next();
2186 String lab = ed.label();
2188 if (tVert.equals(ed.inVertex())) {
2189 vtx = ed.outVertex();
2191 vtx = ed.inVertex();
2194 retArr.add(" >>> COULD NOT FIND VERTEX on the other side of this edge edgeId = " + ed.id() + " <<< ");
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 );
2207 * Show properties for node.
2209 * @param transId the trans id
2210 * @param fromAppId the from app id
2211 * @param tVert the t vert
2212 * @return the array list
2214 private static ArrayList <String> showPropertiesForNode( String transId, String fromAppId, Vertex tVert ){
2216 ArrayList <String> retArr = new ArrayList <> ();
2217 if( tVert == null ){
2218 retArr.add("null Node object passed to showPropertiesForNode()\n");
2221 String nodeType = "";
2222 Object ob = tVert.<Object>property("aai-node-type").orElse(null);
2227 nodeType = ob.toString();
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 + "] ");
2243 private static ArrayList <Vertex> getConnectedNodes(GraphTraversalSource g, Vertex startVtx )
2244 throws AAIException {
2246 ArrayList <Vertex> retArr = new ArrayList <> ();
2247 if( startVtx == null ){
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);
2262 }// End of getConnectedNodes()
2265 private static ArrayList <Vertex> getConnectedChildrenOfOneType( GraphTraversalSource g,
2266 Vertex startVtx, String childNType ) throws AAIException{
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());
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);
2276 String tmpNt = ob.toString();
2277 if( tmpNt.equals(childNType)){
2278 childList.add(tmpVtx);
2285 }// End of getConnectedChildrenOfOneType()
2288 private static Vertex getConnectedParent( GraphTraversalSource g,
2289 Vertex startVtx ) throws AAIException{
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());
2294 while( vertI != null && vertI.hasNext() ){
2295 // Note - there better only be one!
2296 parentVtx = vertI.next();
2301 }// End of getConnectedParent()
2304 private static long figureWindowStartTime( int timeWindowMinutes ){
2305 // Given a window size, calculate what the start-timestamp would be.
2307 if( timeWindowMinutes <= 0 ){
2308 // This just means that there is no window...
2311 long unixTimeNow = System.currentTimeMillis();
2312 long windowInMillis = timeWindowMinutes * 60L * 1000;
2314 long startTimeStamp = unixTimeNow - windowInMillis;
2316 return startTimeStamp;
2317 } // End of figureWindowStartTime()