package jeffaschenk.commons.frameworks.cnxidx.admin; import jeffaschenk.commons.frameworks.cnxidx.utility.commandlinearguments.CommandLinePrincipalCredentials; import jeffaschenk.commons.frameworks.cnxidx.utility.commandlinearguments.idxArgParser; import jeffaschenk.commons.frameworks.cnxidx.utility.commandlinearguments.idxArgVerificationRules; import jeffaschenk.commons.frameworks.cnxidx.utility.commandlinearguments.idxArgVerifier; import jeffaschenk.commons.frameworks.cnxidx.utility.idxLDIFReader; import jeffaschenk.commons.frameworks.cnxidx.utility.idxLogger; import jeffaschenk.commons.frameworks.cnxidx.utility.ldap.*; import java.util.*; import java.util.logging.*; import java.io.*; import javax.naming.*; import javax.naming.directory.*; /** * Java Command line utility, driven from properties and command * line parameters to modify an existing entry within the Directory. * <br> * <b>Usage:</b><br> * IRRmodifyEntry <Required Parameters> <Optional Parameters> * <br> * <b>Required Parameters are:</b> * <pre> * --hosturl * Specify IRR(Directory) LDAP URL, ldap://hostname.acme.com * --irrid * Specify IRR(Directory) LDAP BIND DN, cn=irradmin,o=icosdsa * --irrpw * Specify IRR(Directory) LDAP BIND Password * --idu * Specify FRAMEWORK Keystore Alias to obtain IRRID and IRRPW. * --infile * Specify Full Input file Path of Change Log to be read. * </pre> * <b>Optional Parameters are:</b> * <pre> * --verbose * Specify Additional Logging Information. * --version * Display Version information and exit. * --? * This Display. * * </pre> * * @author jeff.schenk * @version 2.0 $Revision * Developed 2002 */ public class IRRmodifyEntry implements idxCMDReturnCodes { private static String VERSION = "Version: 3.0 2003-09-12, " + "FRAMEWORK, Incorporated."; // ******************************* // Common Logging Facility. public static final String CLASSNAME = IRRmodifyEntry.class.getName(); public static idxLogger IDXLOG = new idxLogger(); private static String MP = CLASSNAME + ": "; // ******************************* // Modification DN and LinkedList String CURRENT_MODDN = ""; LinkedList<ModificationItem> CURRENT_MODLIST = new LinkedList<>(); // ******************************* // Constants. private static final String LDIF_ADD_CHANGETYPE = "add"; private static final String LDIF_DELETE_CHANGETYPE = "delete"; private static final String LDIF_MODRDN_CHANGETYPE = "modrdn"; private static final String LDIF_MODDN_CHANGETYPE = "moddn"; private static final String LDIF_MODIFY_CHANGETYPE = "modify"; private static final String LDIF_ATTRIBUTE_DELETE = "delete"; private static final String LDIF_ATTRIBUTE_REPLACE = "replace"; private static final String LDIF_ATTRIBUTE_ADD = "add"; private static final String NEWRDN = "newrdn"; private static final String DELETEOLDRDN = "deleteoldrdn"; private static final String NEWSUPERIOR = "newsuperior"; private static final String CONTROL = "control"; private static final String TREE_DELETE_CONTROL = "1.2.840.113556.1.4.805"; // **************************** // Runtime Statistics private int entries_exceptions = 0; private int entries_skipped = 0; private int entries_processed = 0; private int entries_modified = 0; private int entries_renamed = 0; private int entries_deleted = 0; private int entries_added = 0; // **************************** // Attribute Statistics private int total_attributes_modified = 0; private int total_attributes_deleted = 0; private int total_attributes_added = 0; private int attributes_modified = 0; private int attributes_deleted = 0; private int attributes_added = 0; // Action status - we may need to try updates more than once // NOTE: this value remaining false will cause infinate looping. private boolean isLdapUpdate = false; // set true if we don't get certain types of errors /** * Usage * Class to print Usage parameters and simple exit. */ static void Usage() { System.err.println(MP + "Usage:"); System.err.println(MP + "IRRmodifyEntry <Required Parameters> <Optional Parameters>"); System.err.println("\n" + MP + "Required Parameters are:"); System.err.println(MP + "--hosturl "); System.err.println("\tSpecify Source IRR(Directory) LDAP URL, ldap://hostname.acme.com"); System.err.println(MP + "--irrid "); System.err.println("\tSpecify Source IRR(Directory) LDAP BIND DN, cn=irradmin,o=icosdsa"); System.err.println(MP + "--irrpw "); System.err.println("\tSpecify Source IRR(Directory) LDAP BIND Password"); System.err.println(MP + "--idu "); System.err.println("\tSpecify FRAMEWORK Keystore Alias to obtain IRRID and IRRPW."); System.err.println(MP + "--infile "); System.err.println("\tSpecify Full File System Path of LDIF Modification Entry."); System.err.println("\n" + MP + "Optional Parameters are:"); System.err.println(MP + "--verbose"); System.err.println("\tSpecify Additional Logging Information."); System.err.println(MP + "--version"); System.err.println("\tDisplay Version information and exit."); System.err.println(MP + "--?"); System.err.println("\tThe Above Display."); System.exit(EXIT_USAGE); } // End of Subclass /** * IRRmodifyEntry Contructor class driven from * Main or other Class Caller. */ public IRRmodifyEntry() { } // End of Constructor for IRRmodifyEntry. /** * Set the correct Message Prefix for this instance of the Function Utility. * * @param _mp Name of Message Prefix. */ public void setMP(String _mp) { if (_mp != null) { MP = _mp + ": "; } } // End of setMP Method. /** * clearStats Method resets all statistics for this utility classs. */ public void clearStats() { entries_exceptions = 0; entries_skipped = 0; entries_processed = 0; entries_modified = 0; entries_renamed = 0; entries_deleted = 0; entries_added = 0; } // End of clearStats Method. /** * obtainStats Method to obtain all statistics for this utility classs. */ public int[] obtainStats() { int[] stats = new int[7]; stats[0] = entries_exceptions; stats[1] = entries_skipped; stats[2] = entries_processed; stats[3] = entries_modified; stats[4] = entries_renamed; stats[5] = entries_deleted; stats[6] = entries_added; return (stats); } // End of clearStats Method. /** * dumpStats Method displays all statistics for this utility. */ public void dumpStats() { String METHODNAME = "dumpStats"; IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Processed: [" + entries_processed + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Exceptions: [" + entries_exceptions + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Skipped: [" + entries_skipped + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Added: [" + entries_added + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Deleted: [" + entries_deleted + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Renamed: [" + entries_renamed + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Entries Modified: [" + entries_modified + "]"); } // End of dumpStats Method. /** * dumpStats Method displays all statistics for this utility. */ public void dumpAttributeStats() { String METHODNAME = "dumpAttributeStats"; IDXLOG.fine(CLASSNAME, METHODNAME, "Total Attributes Added: [" + total_attributes_added + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Total Attributes Deleted: [" + total_attributes_deleted + "]"); IDXLOG.fine(CLASSNAME, METHODNAME, "Total Attributes Modified: [" + total_attributes_modified + "]"); } // End of dumpAttributeStats. /** * perform Method performs the requested IRR Function Utility. * * @param irrctx of Destination Directory where modifications are to be applied. * @param INPUT_FILENAME Full Filename Path of Modification LDIF Structure. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ public void perform(DirContext irrctx, String INPUT_FILENAME) throws Exception, idxIRRException { // **************************************** // Run a Modification Function. String METHODNAME = "perform"; IDXLOG.info(CLASSNAME, METHODNAME, "Entering to perform Modification, Driven by FileName:[" + INPUT_FILENAME + "]."); // **************************************** // Note The Start Time. idxElapsedTime elt = new idxElapsedTime(); // ***************************************** // Initialize. BufferedReader LDIFIN = null; Attributes current_entry_attributes = null; String current_dn = null; String LDIFVersion = null; String IDXCHANGETYPE = null; isLdapUpdate = false; // CPR07763 // The remote host can go away - allow this to happen and not lose transactions. while (!isLdapUpdate) { // until this is set true, loop with a 10 minute delay // reuse the same file over again if needed. // ***************************************** // Catch our Exceptions. try { // ***************************************** // Open up the Input Stream from a File. LDIFIN = new BufferedReader( new FileReader(INPUT_FILENAME), 16384); // ****************************************** // Obtain our Reader Instance with our Input. idxLDIFReader ldif = new idxLDIFReader(LDIFIN); // ****************************************** // Process the Entire Input Stream. while (ldif.hasMore()) { current_entry_attributes = ldif.getNextModEntry(); current_dn = ldif.getCurrentDN(); // **************************************************** // Check for a Pending Modification to Process. if ((!CURRENT_MODLIST.isEmpty()) && (!CURRENT_MODDN.equalsIgnoreCase(current_dn))) { process_pending_modification(irrctx); } // **************************************************** // Skip, if nothing here, could be a seperator. // if ((current_dn == null) || (current_dn.equals("")) || (current_entry_attributes == null)) { continue; } // **************************************************** // Obtain Modification Control Information for Entry. // Attribute myattr = current_entry_attributes.get("changetype"); if (myattr != null) { Object myValue = myattr.get(); IDXCHANGETYPE = ((String) myValue).trim(); current_entry_attributes.remove("changetype"); // *************************************** // If we have a changetype and we have // something pending, then that indicates that // we need to process the new change before // we move on. // if (!CURRENT_MODLIST.isEmpty()) { process_pending_modification(irrctx); } } else if (!CURRENT_MODLIST.isEmpty()) { // ************************************** // If I have a Pending MODLIST, // Assume we are still in that Modification // for the Entry. IDXCHANGETYPE = LDIF_MODIFY_CHANGETYPE; } else { // ************************************** // Assume Changetype is always Add, // if there are no pending Mods. IDXCHANGETYPE = LDIF_ADD_CHANGETYPE; } // End of Else. // ************************************************* // Remove the DN not needed, from the Attribute set. current_entry_attributes.remove("dn"); // **************************************************** // Verify the Change Type. isLdapUpdate = true; // allow it to fail in the following modules // unless something sets the lsLdapUpdate = false, we should run like // normal. if (IDXCHANGETYPE.equalsIgnoreCase(LDIF_ADD_CHANGETYPE)) { addEntry(irrctx, current_dn, current_entry_attributes); } else if (IDXCHANGETYPE.equalsIgnoreCase(LDIF_DELETE_CHANGETYPE)) { deleteEntry(irrctx, current_dn, current_entry_attributes); } else if (IDXCHANGETYPE.equalsIgnoreCase(LDIF_MODIFY_CHANGETYPE)) { modifyEntry(irrctx, current_dn, current_entry_attributes); } else if ((IDXCHANGETYPE.equalsIgnoreCase(LDIF_MODRDN_CHANGETYPE)) || (IDXCHANGETYPE.equalsIgnoreCase(LDIF_MODDN_CHANGETYPE))) { modDNEntry(irrctx, current_dn, current_entry_attributes); } // **************************************************** // Falling through indicates we have an invalid // Change Type, report it and continue. // else { IDXLOG.warning(CLASSNAME, METHODNAME, "Unknown Change Type Encountered:[" + IDXCHANGETYPE + "] for DN:[" + current_dn + "], Entry bypassed."); entries_exceptions++; // ******************************** // Ignore the Entry. continue; } // End of Else. } // End of LDIF Input While Loop. // **************************************************** // Check for a Pending Modification to Process. if (!CURRENT_MODLIST.isEmpty()) { process_pending_modification(irrctx); } } catch (Exception e) { IDXLOG.severe(CLASSNAME, METHODNAME, "Exception during processing LDIF Input. " + e); e.printStackTrace(); throw e; } // End of exception // *************************************** // Close our Input File. try { LDIFIN.close(); } catch (Exception e) { IDXLOG.severe(CLASSNAME, METHODNAME, "Exception closing LDIF Input. " + e); e.printStackTrace(); throw e; } // End of exception // should we re-try this again? - if isLdapUpdate = false, then we failed if (!isLdapUpdate) { // wait 10 minutes and try again // 10 minutes = 1000 * 60 * 10 milliseconds long waitTime = 1000 * 60 * 10; try { Thread.sleep(waitTime); } catch (InterruptedException ie) { } } else { break; // it did work. } } // close While Loop // **************************************** // Any Entries Restored? if (entries_processed == 0) { IDXLOG.warning(CLASSNAME, METHODNAME, "No Entries were Processed."); } // End of If. // **************************************** // Note The End Time. elt.setEnd(); // **************************************** // Show Modification Timings IDXLOG.info(CLASSNAME, METHODNAME, "Modification Complete using FileName:[" + INPUT_FILENAME + "], Elapsed Time: " + elt.getElapsed()); } // End of Perform Method. /** * addEntry, Performs add of Entry. * * @param irrctx of Destination Directory where modifications are to be applied. * @param current_dn Current DN. * @param current_entry_attributes Current set of Modification Attributes. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void addEntry(DirContext irrctx, String current_dn, Attributes current_entry_attributes) throws Exception, idxIRRException { String METHODNAME = "addEntry"; IDXLOG.fine(CLASSNAME, METHODNAME, "Processing Add for DN:[" + current_dn + "]."); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("ADD ENTRY WITH ATTRIBUTE", current_entry_attributes); } // End of Check for Logging. // **************************** // Count the Entries Processed entries_processed++; // *********************************** // Perform the Add/bind of the Entry. try { irrctx.bind(current_dn, null, current_entry_attributes); entries_added++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Added for DN:[" + current_dn + "]."); isLdapUpdate = true; } catch (NameAlreadyBoundException e) { IDXLOG.warning(CLASSNAME, METHODNAME, "DN Already Bound for add/bind operation, DN:[" + current_dn + "], " + e); entries_exceptions++; isLdapUpdate = true; } catch (Exception e_bind) { IDXLOG.warning(CLASSNAME, METHODNAME, "Error Performing add/bind operation for DN:[" + current_dn + "], " + e_bind); IDXLOG.warning(CLASSNAME, METHODNAME, "LDIF Sync function will wait 10 minutes and " + "try [" + current_dn + "] again"); isLdapUpdate = false; entries_exceptions++; } // End of Exception Processing. } // End of addEntry Method. /** * deleteEntry, Performs delete of Entry. * * @param irrctx of Destination Directory where modifications are to be applied. * @param current_dn Current DN. * @param current_entry_attributes Current set of Modification Attributes. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void deleteEntry(DirContext irrctx, String current_dn, Attributes current_entry_attributes) throws Exception, idxIRRException { String METHODNAME = "deleteEntry"; IDXLOG.fine(CLASSNAME, METHODNAME, "Processing Delete for DN:[" + current_dn + "]."); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("DELETE ENTRY", current_entry_attributes); } // End of Check for Logging. // **************************** // Count the Entries Processed entries_processed++; // ******************************* // Obtain Additional Values for // performing the rename. // Attribute eo = null; // ************************************ // Eventually there is to be a // new function to perform a // tree delete control. // See RFC2849. // // Now check for that TREE Deletion // control. // ************************************ boolean deletetree = false; String _control = ( (eo = current_entry_attributes.get(CONTROL)) != null ? (String) eo.get() : null); if ((_control != null) && (_control.startsWith(TREE_DELETE_CONTROL))) { _control = _control.trim().toLowerCase(); if (_control.endsWith("true")) { deletetree = true; } } // End of If. // *************************************** // Perform a Tree Delete if Required // Based upon control. if (deletetree) { // **************************************** // Initailize Constructor. IRRdeleteEntry deleteFUNCTION = new IRRdeleteEntry(); // **************************************** // Perform Function. try { deleteFUNCTION.perform(irrctx, current_dn, true, false, false); entries_deleted++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry and Tree Deleted for DN:[" + current_dn + "]."); isLdapUpdate = true; } catch (Exception et) { IDXLOG.warning(CLASSNAME, METHODNAME, "Error Performing delete/unbind on entire tree operation for DN:[" + current_dn + "], " + et); IDXLOG.warning(CLASSNAME, METHODNAME, "LDIF Sync function will wait 10 minutes and " + "try [" + current_dn + "] again"); isLdapUpdate = false; entries_exceptions++; } // End of Exception Processing. } // End of Delete Tree. else { // ***************************** // Perform the delete/unbind of // the single Entry. try { irrctx.unbind(current_dn); entries_deleted++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Deleted for DN:[" + current_dn + "]."); isLdapUpdate = true; } catch (NameNotFoundException e) { IDXLOG.warning(CLASSNAME, METHODNAME, "DN not found for delete/unbind operation for DN:[" + current_dn + "], " + e); isLdapUpdate = true; entries_exceptions++; } catch (Exception e_unbind) { IDXLOG.warning(CLASSNAME, METHODNAME, "Error Performing delete/unbind operation for DN:[" + current_dn + "], " + e_unbind); IDXLOG.warning(CLASSNAME, METHODNAME, "LDIF Sync function will wait 10 minutes and " + "try [" + current_dn + "] again"); isLdapUpdate = false; entries_exceptions++; } // End of Exception Processing. } // End of Else. } // End of deleteEntry Method. /** * modDNEntry, Performs Rename/MODRDN/MODDN of Entry. * * @param irrctx of Destination Directory where modifications are to be applied. * @param current_dn Current DN. * @param current_entry_attributes Current set of Modification Attributes. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void modDNEntry(DirContext irrctx, String current_dn, Attributes current_entry_attributes) throws Exception, idxIRRException { String METHODNAME = "modDNEntry"; IDXLOG.fine(CLASSNAME, METHODNAME, "Processing Rename for DN:[" + current_dn + "]."); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("RENAME ENTRY", current_entry_attributes); } // End of Check for Logging. isLdapUpdate = true; // **************************** // Count the Entries Processed entries_processed++; // ******************************* // Obtain Additional Values for // performing the rename. // Attribute eo = null; // ********************* // New RDN. String _newrdn = ( (eo = current_entry_attributes.get(NEWRDN)) != null ? (String) eo.get() : null); if ((_newrdn == null) || (_newrdn.equalsIgnoreCase(""))) { IDXLOG.warning(CLASSNAME, METHODNAME, "No newrdn value specified for modrdn/rename operation for DN:[" + current_dn + "]."); entries_exceptions++; return; } // End of if. // ************************* // Delete Old RDN Indicator. boolean deleteoldrdn = true; String _deleteoldrdn = ( (eo = current_entry_attributes.get(DELETEOLDRDN)) != null ? (String) eo.get() : null); if ((_deleteoldrdn == null) || (_deleteoldrdn.equalsIgnoreCase("0"))) { deleteoldrdn = false; } // ********************* // New Superior. String _newsuperior = ( (eo = current_entry_attributes.get(NEWSUPERIOR)) != null ? (String) eo.get() : null); // ********************* // Formulate the new DN String _newDN = _newrdn; if ((_newsuperior != null) && (!_newsuperior.equalsIgnoreCase(""))) { _newDN = _newDN + "," + _newsuperior; } else { idxParseDN zDN = new idxParseDN(current_dn); if (!zDN.isValid()) { IDXLOG.warning(CLASSNAME, METHODNAME, "Existing DN value specified for modrdn/rename operation is Invalid, DN:[" + current_dn + "]."); entries_exceptions++; return; } // End of If. // ************************ // Now Construct new DN. if (!zDN.getPDN().equalsIgnoreCase("")) { _newDN = _newDN + "," + zDN.getPDN(); } } // End of Else. // *********************************** // Perform the ModDN/ModRDN/Rename // of the Entry. IDXLOG.fine(CLASSNAME, METHODNAME, "Formulated New DN:[" + _newDN + "]."); // ************************* // Determine if we have a // simple Rename or a Copy. if (deleteoldrdn) { // *********************************** // Perform the Add/bind of the Entry. try { irrctx.rename(current_dn, _newDN); entries_renamed++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Renamed New DN:[" + _newDN + "]."); isLdapUpdate = true; } catch (NameAlreadyBoundException e) { IDXLOG.warning(CLASSNAME, METHODNAME, "DN Already Bound for modrdn/rename operation, Old DN:[" + current_dn + "]," + "New DN:[" + _newDN + "], " + e); isLdapUpdate = true; entries_exceptions++; } catch (Exception e_bind) { IDXLOG.warning(CLASSNAME, METHODNAME, "Error Performing modrdn/rename operation for Old DN:[" + current_dn + "], " + "New DN:[" + _newDN + "], " + e_bind); isLdapUpdate = true; entries_exceptions++; } // End of Exception Processing. // **************************** // Since the delete old rdn // is false, we must perform // a copy from one container // to the other. // } else { // **************************************** // Initialize Constructor. IRRcopyEntry copyFUNCTION = new IRRcopyEntry(); // **************************************** // Perform Function. try { copyFUNCTION.perform(irrctx, current_dn, irrctx, _newDN, false, true, false, false); entries_renamed++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Renamed New DN:[" + _newDN + "], leaving existing DN in place."); isLdapUpdate = true; } catch (Exception e_bind) { IDXLOG.warning(CLASSNAME, METHODNAME, "Error Performing modrdn/rename (copy) operation for Old DN:[" + current_dn + "], " + "New DN:[" + _newDN + "], " + e_bind); isLdapUpdate = true; entries_exceptions++; } // End of Exception Processing. } // End of Else. } // End of modDNEntry Method. /** * modifyEntry, Performs Modifications on Entry. * * @param irrctx of Destination Directory where modifications are to be applied. * @param current_dn Current DN. * @param current_entry_attributes Current set of Modification Attributes. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void modifyEntry(DirContext irrctx, String current_dn, Attributes current_entry_attributes) throws Exception, idxIRRException { String METHODNAME = "modifyEntry"; isLdapUpdate = true; // never test for failure in this module, so assume it will get trapped elsewhere // ********************************* // Only Show the Processing of the // Modify for the Entry once. if ((CURRENT_MODDN == null) || (CURRENT_MODDN.equalsIgnoreCase(""))) { IDXLOG.fine(CLASSNAME, METHODNAME, "Processing Modify for DN:[" + current_dn + "]."); } // **************************************************** // Ok, now we have a valid changetype for Entry, // now determine which attributes are added, replaced // or deleted. Obtain all of the maintenance Contructs. // Attribute Attribute_Deletions = current_entry_attributes.get(LDIF_ATTRIBUTE_DELETE); current_entry_attributes.remove(LDIF_ATTRIBUTE_DELETE); Attributes Value_Deletions = obtainModifications(Attribute_Deletions, current_entry_attributes, true); Attribute Attribute_Replacements = current_entry_attributes.get(LDIF_ATTRIBUTE_REPLACE); current_entry_attributes.remove(LDIF_ATTRIBUTE_REPLACE); Attributes Value_Replacements = obtainModifications(Attribute_Replacements, current_entry_attributes, false); Attribute Attribute_Additions = current_entry_attributes.get(LDIF_ATTRIBUTE_ADD); current_entry_attributes.remove(LDIF_ATTRIBUTE_ADD); Attributes Value_Additions = obtainModifications(Attribute_Additions, current_entry_attributes, false); // ******************************************* // Show Current DN and the Attributes. IDXLOG.finest(CLASSNAME, METHODNAME, "Queueing Modification Set for DN:[" + current_dn + "]."); // ******************************************* // Show Possible Attribute Deletions. if (Value_Deletions != null) { attributes_deleted = Value_Deletions.size(); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("DELETE ATTRIBUTE", Value_Deletions); } // End of FINEST Check. } // End of If. // ******************************************* // Show Possible Attribute Replacements. if (Value_Replacements != null) { attributes_modified = Value_Replacements.size(); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("REPLACE ATTRIBUTE", Value_Replacements); } // End of Verbosity. } // End of If. // ******************************************* // Show Possible Attribute Additions. if (Value_Additions != null) { attributes_added = Value_Additions.size(); if (IDXLOG.logger.isLoggable(Level.FINEST)) { showModification("ADD ATTRIBUTE", Value_Additions); } // End of Verbosity. } // End of If. // ******************************************* // Setup the Deletions. if (Value_Deletions != null) { CURRENT_MODDN = current_dn; setupModifications(irrctx.REMOVE_ATTRIBUTE, Value_Deletions); } // End of If. // ******************************************* // Setup the Replacements. if (Value_Replacements != null) { CURRENT_MODDN = current_dn; setupModifications(irrctx.REPLACE_ATTRIBUTE, Value_Replacements); } // End of If. // ******************************************* // Setup the Additions. if (Value_Additions != null) { CURRENT_MODDN = current_dn; setupModifications(irrctx.ADD_ATTRIBUTE, Value_Additions); } // End of If. // ********************************************* // Ok this ModEntry has been Queued for the // Overall modficiation of the Entry, // when the DN Changes or we run out of // changes this queued change will be // run. return; } // End of modifyEntry method. /** * process pending modifications that were sequentially queued to process in bluk. * * @param irrctx of Destination Directory where modifications are to be applied. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void process_pending_modification(DirContext irrctx) throws Exception, idxIRRException { String METHODNAME = "process_pending_modification"; IDXLOG.finer(CLASSNAME, METHODNAME, "Processing Pending Modification for DN:[" + CURRENT_MODDN + "]."); // **************************** // Count the Entries Processed entries_processed++; // **************************** // Check the Queue. if ((CURRENT_MODDN == null) || (CURRENT_MODDN.equalsIgnoreCase("")) || (CURRENT_MODLIST.isEmpty())) { resetModList(); IDXLOG.warning(CLASSNAME, METHODNAME, "No Pending Modifications Found for DN:[" + CURRENT_MODDN + "]."); } // End of Pending Check. // **************************************************** // Now Create a new Modification Item List for the // Entry and then process it. ModificationItem[] EntryModSet = new ModificationItem[CURRENT_MODLIST.size()]; // **************************************************** // Clear some counters. attributes_modified = 0; attributes_deleted = 0; attributes_added = 0; // ******************************************* // Iterate through the List List for the // Entry. for (int modItem = 0; modItem < CURRENT_MODLIST.size(); modItem++) { EntryModSet[modItem] = (ModificationItem) CURRENT_MODLIST.get(modItem); // *************************** // Count the Mods. if (EntryModSet[modItem].getModificationOp() == irrctx.ADD_ATTRIBUTE) { attributes_added++; } else if (EntryModSet[modItem].getModificationOp() == irrctx.REPLACE_ATTRIBUTE) { attributes_modified++; } else if (EntryModSet[modItem].getModificationOp() == irrctx.REMOVE_ATTRIBUTE) { attributes_deleted++; } } // End of For Loop. // ******************************************* // Now Apply the Modifications to the Entry. try { irrctx.modifyAttributes(CURRENT_MODDN, EntryModSet); entries_modified++; IDXLOG.fine(CLASSNAME, METHODNAME, "Entry Modified DN:[" + CURRENT_MODDN + "]."); // ******************************************** // Show the stats for the Entry. if (attributes_added > 0) { IDXLOG.finer(CLASSNAME, METHODNAME, "Entry Attributes Added: [" + attributes_added + "]"); } if (attributes_deleted > 0) { IDXLOG.finer(CLASSNAME, METHODNAME, "Entry Attributes Deleted: [" + attributes_deleted + "]"); } if (attributes_modified > 0) { IDXLOG.finer(CLASSNAME, METHODNAME, "Entry Attributes Modified: [" + attributes_modified + "]"); } // *************************************************** // Accumulate the Totals for the Maintenance Activity. total_attributes_modified = total_attributes_modified + attributes_modified; total_attributes_deleted = total_attributes_deleted + attributes_deleted; total_attributes_added = total_attributes_added + attributes_added; } catch (NameNotFoundException nnfe) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "Entry Not Found for DN:[" + CURRENT_MODDN + "], Ignoring Modification."); } catch (AttributeInUseException aiu) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "Attribute already exists for DN:[" + CURRENT_MODDN + "], use Replace to properly Modify Entry."); } catch (NoSuchAttributeException nsa) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "Attribute and/or Value does not exist for DN:[" + CURRENT_MODDN + "], Ignoring Modification for Entry."); } catch (AttributeModificationException ame) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "Attribute Modification Exception occurred for DN:[" + CURRENT_MODDN + "]," + ame); } catch (NamingException ne) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "IRR Exception occurred for DN:[" + CURRENT_MODDN + "]," + ne); } catch (Exception e) { entries_exceptions++; IDXLOG.warning(CLASSNAME, METHODNAME, "Exception Processing Modification Set for DN:[" + CURRENT_MODDN + "]," + e); } // End of exception. // ********************************** // Clear Modification List. resetModList(); } // End of process_pending_modification method. /** * resetModList, clears and resets Mod List for next possible Entry Modification. */ private void resetModList() { String METHODNAME = "resetModList"; // **************************** // Clear the LinkedList and DN. CURRENT_MODDN = ""; CURRENT_MODLIST.clear(); } // End of resetModList Method. /** * obtainModifications, Obtains Modifications and places into Attributes Object * for processing. * * @param _modattr Modifcation Attribute Driver. * @param _entryattrs Complete set of Modifications. * @param _theseAreDeletions Indicates Modifications are deletions or not. * @return Attributes Generated based only on incoming Attribute. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private Attributes obtainModifications(Attribute _modattr, Attributes _entryattrs, boolean _theseAreDeletions) throws Exception, idxIRRException { // ********************************************** // Check incoming Parameters. if ((_modattr == null) || (_entryattrs == null)) { return (null); } // ********************************************** // Initialize. Attributes _valueModifications = new BasicAttributes(true); // ********************************************** // Loop through the Attribute Driver Values. for (NamingEnumeration ev = _modattr.getAll(); ev.hasMore(); ) { Object Aobject = ev.next(); String Aname = (String) Aobject; Attribute modentry = _entryattrs.get(Aname); if (modentry != null) { _valueModifications.put(modentry); } else if (_theseAreDeletions) { _valueModifications.put(new BasicAttribute(Aname)); } else { // NOOP // Simple Ignore adding information if this // is a replace or add. ; } // End of Else. } // End of Outer For Loop. // ********************************************** // Return Categorized Modifications. return (_valueModifications); } // End of obtainModifications Method. /** * setupModifications, Setup Bulk Modificationitem from incoming Attributes Object. * * @param _modtype Modifcation Type. * @param _entryattrs Incoming attributes to be placed into Modificationitem Queue. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void setupModifications(int _modtype, Attributes _entryattrs) throws Exception, idxIRRException { // ********************************************** // Check incoming Parameters. if (_entryattrs == null) { return; } // ********************************************************** // Process the incoming Attributes into the Modification Set. for (NamingEnumeration ea = _entryattrs.getAll(); ea.hasMore(); ) { Attribute attr = (Attribute) ea.next(); CURRENT_MODLIST.addLast(new ModificationItem(_modtype, attr)); } // End of For Loop. // ********************************************** // Return return; } // End of setupModifications Method. /** * showModification, provides display of modification for Entry. * * @param _operation Operation Performed. * @param _entryattrs to be displayed. * @throws idxIRRException for any specific IRR unrecoverable errors during function. * @throws Exception for any unrecoverable errors during function. */ private void showModification(String _operation, Attributes _entryattrs) throws Exception, idxIRRException { String METHODNAME = "showModification"; // ********************************************** // Show the incoming Modification Set. for (NamingEnumeration ea = _entryattrs.getAll(); ea.hasMore(); ) { Attribute attr = (Attribute) ea.next(); if (attr.size() == 0) { IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + attr.getID() + ": "); continue; } // End of If. // ********************************************** // Loop through the Attribute Values. for (NamingEnumeration ev = attr.getAll(); ev.hasMore(); ) { String Aname = attr.getID(); Aname = Aname.toLowerCase(); Object Aobject = ev.next(); if (Aname.startsWith("userpassword")) { // ***************************** // Show A trimmed Down Version. IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + "UserPassword: " + "********"); } else if (Aobject instanceof byte[]) { IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + attr.getID() + ": [ Binary data " + ((byte[]) Aobject).length + " in length ]"); } else if (Aobject instanceof String) { if (((String) Aobject).length() > 64) { IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + attr.getID() + ": " + ((String) Aobject).substring(0, 10) + " ..... " + (((String) Aobject).substring(((String) Aobject).length() - 10)) + ", Full Length:[" + ((String) Aobject).length() + "]"); } else { IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + attr.getID() + ": " + Aobject); } // End of Inner Else. } else { // Show normal Attributes as is... IDXLOG.finest(CLASSNAME, METHODNAME, _operation + "-> " + attr.getID() + ": " + "UNKNOWN TYPE, NOT BINARY or STRING"); } // End of Else. } // End of Inner For Loop. } // End of Outer For Loop } // End of showModification Method. /** * Main * * @param args Incoming Argument Array. * @see jeffaschenk.commons.frameworks.cnxidx.admin.IRRmodifyEntry */ public static void main(String[] args) { idxManageContext IRRSource = null; String IRRHost = null; String IRRPrincipal = null; String IRRCredentials = null; String INPUT_FILENAME = null; boolean VERBOSE = false; String METHODNAME = "main"; // **************************************** // Send the Greeting. System.out.println(MP + VERSION); // **************************************** // Parse the incoming Arguments and // create objects for each entity. // idxArgParser Zin = new idxArgParser(); Zin.parse(args); // *************************************** // Do I have any unnamed Values? if (!Zin.IsUnNamedEmpty()) { System.out.println(MP + "Unknown Values Encountered, Terminating Process."); Zin.showUnNamed(); Usage(); } // End of If. // *************************************** // Was Version Info Requested? if (Zin.doesNameExist("version")) { System.exit(EXIT_VERSION); } // *************************************** // Was Help Info Requested? if ((Zin.doesNameExist("?")) || (Zin.doesNameExist("usage"))) { Usage(); } // *************************************** // Was Verbosity Requested? if (Zin.doesNameExist("verbose")) { VERBOSE = true; } // *************************************** // Show Arguments if Verbose Selected. if (VERBOSE) { Zin.show(); } // *************************************** // Build our verification Rule Set. // // idxArgVerificationRules Parameters are: // String Name of argument name. // Boolean Required Argument Indicator. // Boolean StringObject Argument Indicator. // String Name of Value Verification Routine. // LinkedList<idxArgVerificationRules> VAR = new LinkedList<>(); VAR.add(new idxArgVerificationRules("hosturl", true, true)); VAR.add(new idxArgVerificationRules("irrid", false, true)); VAR.add(new idxArgVerificationRules("irrpw", false, true)); VAR.add(new idxArgVerificationRules("idu", false, true)); VAR.add(new idxArgVerificationRules("infile", true, true)); VAR.add(new idxArgVerificationRules("verbose", false, false)); // *************************************** // Run the Verification Rule Set. // If we do not have a positive return, // then an invalid argument was detected, // so show Usage and die. // idxArgVerifier AV = new idxArgVerifier(); AV.setVerbose(VERBOSE); if (!AV.Verify(MP, Zin, VAR)) { Usage(); } // *************************************** // Obtain Authentication Principal and // Credentials from the KeyStore or // the command line. // CommandLinePrincipalCredentials clPC = new CommandLinePrincipalCredentials(Zin); // ************************************************** // Load up the Principal/Credentials. // if (clPC.wasObtained()) { IRRPrincipal = clPC.getPrincipal(); System.out.println(MP + "IRR ID:[" + IRRPrincipal + "]"); IRRCredentials = clPC.getCredentials(); //System.out.println(MP+"IRR Password:["+IRRCredentials+"]"); } else { System.out.println(MP + "Required Principal and Credentials not Specified, unable to continue."); Usage(); } // End of Else. // ************************************************** // Load up the RunTime Arguments. // IRRHost = (String) Zin.getValue("hosturl"); System.out.println(MP + "IRR Host URL:[" + IRRHost + "]"); INPUT_FILENAME = ((String) Zin.getValue("infile")).trim(); System.out.println(MP + "Input File:[" + INPUT_FILENAME + "]"); // **************************************** // Note The Start Time. idxElapsedTime elt = new idxElapsedTime(); // *********************************************** // Now initiate a Connection to the Directory // for a LDAP Source Context System.out.println(MP + "Attempting Source Directory Connection to Host URL:[" + IRRHost + "]"); IRRSource = new idxManageContext(IRRHost, IRRPrincipal, IRRCredentials, "ModificationEntry Source"); // ************************************************ // Exit on all Exceptions. IRRSource.setExitOnException(true); // ************************************************ // Now Try to Open and Obtain Context. try { IRRSource.open(); } catch (Exception e) { System.err.println(MP + e); System.exit(EXIT_IRR_UNABLE_TO_OBTAIN_CONTEXT); } // End of exception // ************************************************ // Disable Factories. try { IRRSource.disableDSAEFactories(); } catch (Exception e) { System.err.println(MP + e); System.exit(EXIT_GENERIC_FAILURE); } // End of exception // **************************************** // Initailize Constructor. IRRmodifyEntry FUNCTION = new IRRmodifyEntry(); // **************************************** // Perform Function. try { FUNCTION.perform(IRRSource.irrctx, INPUT_FILENAME); // **************************************** // Show the Statistics FUNCTION.dumpStats(); FUNCTION.dumpAttributeStats(); } catch (Exception e) { System.err.println(MP + "IRR Exception Performing IRRmodifyEntry.\n" + e); System.exit(EXIT_GENERIC_FAILURE); } // End of Exception. // *************************************** // Close up Shop. System.out.println(MP + "Closing Destination Directory Context."); try { IRRSource.close(); } catch (Exception e) { System.err.println(e); System.exit(EXIT_IRR_CLOSE_FAILURE); } // End of exception // **************************************** // Note The End Time. elt.setEnd(); // **************************************** // Exit System.out.println(MP + "Done, Elapsed Time: " + elt.getElapsed()); System.exit(EXIT_SUCCESSFUL); } // End of Main } // End of Class IRRmodifyEntry