package jeffaschenk.commons.frameworks.cnxidx.admin; import jeffaschenk.commons.frameworks.cnxidx.utility.ldap.*; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import java.io.*; import javax.naming.*; import javax.naming.directory.*; /** * Java Daemon Server Thread. * * @author jeff.schenk * @version 2.0 $Revision * Developed 2001-2003 */ /** * IRRChangeCollector * Class to run Entry Collector and LDIF Writer Thread. */ class IRRChangeCollector implements Runnable, idxCMDReturnCodes { /** * IRRChangeCollector * Class to provide Entry Collector and Writer LDIF Thread from Entries obtain via pipe. */ Thread t; private BufferedReader CRQ; private IRRChangeStatus ChangeStatus; private static final String MP = "IRRChangeWriter: "; private idxManageContext IRRSource = null; private static String IRRHost = null; private static String IRRPrincipal = null; private static String IRRCredentials = null; private static String IRRMLTuid = null; private static String OUTPUT_DIRECTORY = null; private static boolean VERBOSE = false; private static boolean DEBUG = false; private static boolean APPEND = false; private static int OBUFSIZE = 0; private static long FILETIMESPAN = 0; private idxTimeStamp CurrentTimeStamp = new idxTimeStamp(); private String CurrentActiveFileName = null; private boolean ExitOnException = false; private static final String ObjectClassName = "objectclass"; /** * IRRbackupOutput Contructor class driven. * * @param _BPQin Pipe. * @param _ChangeStatus * @param _IRRHost Source IRR LDAP URL. * @param _IRRPrincipal Source IRR Principal. * @param _IRRCredentials Source IRR Credentials. * @param _IRRMLTuid Source IRR MetaLink Trigger UID. * @param _OUTPUT_DIRECTORY Destination Output Filename. * @param _OBUFSIZE BufferedWriter Output Buffer Size. * @param _FILETIMESPAN File Time Span Window. * @param _APPEND Indicate if Output Should be Appended. * @param _VERBOSE Indicate Verbosity. * @param _DEBUG Indicate DEBUGGING. * @param _ExitOnException Indicate Exit on Exceptions. */ IRRChangeCollector(Reader _BPQin, IRRChangeStatus _ChangeStatus, String _IRRHost, String _IRRPrincipal, String _IRRCredentials, String _IRRMLTuid, String _OUTPUT_DIRECTORY, int _OBUFSIZE, long _FILETIMESPAN, boolean _APPEND, boolean _VERBOSE, boolean _DEBUG, boolean _ExitOnException) { // **************************************** // Set My Incoming Parameters. // IRRHost = _IRRHost; IRRPrincipal = _IRRPrincipal; IRRCredentials = _IRRCredentials; IRRMLTuid = _IRRMLTuid; OUTPUT_DIRECTORY = _OUTPUT_DIRECTORY; OBUFSIZE = _OBUFSIZE; FILETIMESPAN = _FILETIMESPAN; APPEND = _APPEND; VERBOSE = _VERBOSE; DEBUG = _DEBUG; ExitOnException = _ExitOnException; ChangeStatus = _ChangeStatus; // **************************************** // Ready the Synchronized Object and start // the Thread. // this.CRQ = new BufferedReader(_BPQin); t = new Thread(this, "IRRChangeCollector"); t.start(); // Start the Thread } // End of Contructor. /** * run * Thread to Output LDIF Backup Image. */ public void run() { // *********************************************** // Initialize long memfree; String ZEN = null; int DNcount = 0; int OutputCycle = 0; long OutputCycleStart = System.currentTimeMillis(); long OutputCycleStop = 0; long OutputCycleDuration = 0; String tname = Thread.currentThread().getName(); CurrentTimeStamp.enableLocalTime(); // Show Local Time Not GMT. System.out.println(MP + CurrentTimeStamp.getFTS() + " Thread Established for:[" + tname + "]"); // *********************************************** // Initialize my LAP Timers idxLapTime LP_ENTRY_SEARCH = new idxLapTime(); idxLapTime LP_ENTRY_TO_LDIF = new idxLapTime(); idxLapTime LP_ENTRY_FROM_PIPE = new idxLapTime(); idxLapTime LP_XML_TO_OBJECT = new idxLapTime(); // *********************************************** // Construct the JDOM NonValidating SAXBuilder. org.jdom.input.SAXBuilder IRRsaxBuilder = new org.jdom.input.SAXBuilder(); // *********************************************** // Now initiate a Connection to the Directory // for a LDAP Source Context System.out.println(MP + CurrentTimeStamp.getFTS() + " Attempting Source Object Directory Connection to Host URL:[" + IRRHost + "]"); IRRSource = new idxManageContext(IRRHost, IRRPrincipal, IRRCredentials, "IRRChangeCollector Source"); // ************************************************ // Exit on all Exceptions. IRRSource.setExitOnException(ExitOnException); // ************************************************ // Now Try to Open and Obtain Context. try { IRRSource.open(); } catch (Exception e) { System.err.println(MP + e); ChangeStatus.setError(EXIT_IRR_CLOSE_FAILURE); return; } // End of exception // ***************************************** // Disable the Factories. try { IRRSource.disableDSAEFactories(); } catch (Exception e) { System.err.println(MP + e); ChangeStatus.setError(EXIT_GENERIC_FAILURE); return; } // End of exception // ************************************************** // Obtain Runtime Object. Runtime rt = Runtime.getRuntime(); //****************************************** // Set up our Search Filter. String SearchFilter = "(objectclass=*)"; //****************************************** // Set up our Search Controls. String[] ALL_AttrIDs = {"*"}; SearchControls OS_ctls = new SearchControls(); OS_ctls.setReturningAttributes(ALL_AttrIDs); OS_ctls.setSearchScope(SearchControls.OBJECT_SCOPE); // ************************************** // Open up the File Output Stream. BufferedWriter LDIFOUT = null; try { // *********************************** // Check for the Directory Existence. // If not available, create the // Directory. File outdir = new File(OUTPUT_DIRECTORY); if (!outdir.exists()) { outdir.mkdirs(); } else if (!outdir.isDirectory()) { System.err.println(MP + "Unable to create Backup Output Directory Path for Incremental Change LDIF Output Files. "); ChangeStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); return; // End Thread. } // End of If. // ************************************ // Obtain our LDIF Log File. OutputCycle++; LDIFOUT = OpenLDIFOutput(OUTPUT_DIRECTORY, OutputCycle, OBUFSIZE); } catch (Exception e) { System.err.println(MP + "Exception opening Incremental Change LDIF Output File. " + e); ChangeStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); return; // End Thread. } // End of exception // ************************************** // Loop to process commands from Walker // Thread. try { while (true) { LP_ENTRY_FROM_PIPE.Start(); ZEN = CRQ.readLine(); LP_ENTRY_FROM_PIPE.Stop(); // *************************** // Ignore Null. if ((ZEN == null) || ("".equals(ZEN))) { continue; } // *************************** // Incoming Request must be // a valid XML Document. // Parse it and determine // processing. // Document Zdoc = null; Element Zeroot = null; try { LP_XML_TO_OBJECT.Start(); // **************************************** // Prep String data to XML Reader. InputStream in = new ByteArrayInputStream((byte[]) ZEN.getBytes()); // **************************************** // Parse the Request. Zdoc = IRRsaxBuilder.build(in); // **************************************** // Obtain the Element Root. if (Zdoc != null) { Zeroot = Zdoc.getRootElement(); } LP_XML_TO_OBJECT.Stop(); } catch (JDOMException e) { System.err.println(MP + "JDOM Exception Processing XML Document, " + e); ChangeStatus.setError(EXIT_GENERIC_FAILURE); return; // End Thread. } // End of Exception. // *************************** // Ignore a Root Element. if (Zeroot == null) { continue; } // *************************** // Check to see if this is a // Process Notification. // if (Zeroot.getName().equalsIgnoreCase("irrchangeloggerprocess")) { // ********************************** // End of cycle? String EOC = Zeroot.getAttributeValue("endofcycle"); if ((EOC != null) && ((EOC.equalsIgnoreCase("yes")) || (EOC.equalsIgnoreCase("true")))) { // **************************** // Process the End of the Cycle. try { // ******************************************* // First Determine if we have elasped the // Duration window, if not, simple flush output // and continue. // OutputCycleStop = System.currentTimeMillis(); OutputCycleDuration = (OutputCycleStop - OutputCycleStart); if (OutputCycleDuration >= FILETIMESPAN) { CloseLDIFOutput(LDIFOUT, OUTPUT_DIRECTORY, OutputCycle, DNcount); OutputCycle++; OutputCycleStart = System.currentTimeMillis(); DNcount = 0; LDIFOUT = OpenLDIFOutput(OUTPUT_DIRECTORY, OutputCycle, OBUFSIZE); } else { // ************************************ // We have not met our Window, so // simple flush existing output, but // do not cycle a new file. // LDIFOUT.flush(); } // End of Else. continue; } catch (Exception e) { System.err.println(MP + "Exception opening Incremental Change LDIF Output File. " + e); ChangeStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); return; // End Thread. } // End of exception } // End of if. // ********************************** // End of Process? String EOP = Zeroot.getAttributeValue("endofprocess"); if ((EOP != null) && ((EOP.equalsIgnoreCase("yes")) || (EOP.equalsIgnoreCase("true")))) { break; } // ********************************** // Ok, Nothing to do, continue... continue; } // End of Process Notification if. // *************************** // Check to see if this is a // Change Notification. // else if (!Zeroot.getName().equalsIgnoreCase("irrchange")) { System.out.println(MP + CurrentTimeStamp.getFTS() + " Warning ** Unknown Process Event detected:[" + Zeroot.getName() + "], Ignoring."); continue; } // End of Unknown Notification. // ****************************************** // Now we do have a change notification, // Extract the Information and process. // // ChangeTypes of Notification is: // 1: Add // 2: Modify // 3: Delete // 4: Rename (moddn or modrdn). // String ChangeTypeName = Zeroot.getAttributeValue("typename"); String ChangeType = Zeroot.getAttributeValue("type"); String cDN = Zeroot.getAttributeValue("dn"); String coldDN = Zeroot.getAttributeValue("olddn"); if ((ChangeTypeName == null) || (ChangeType == null) || (cDN == null)) { System.out.println(MP + CurrentTimeStamp.getFTS() + " Warning ** Required Information for Change not Present" + ", Ignoring Event."); continue; } // End of Unknown Notification. // ****************************************** // Generate a Header for the Change. // LDIFOUT.write("# ***********************************************\n"); LDIFOUT.write("# Change Type:[" + ChangeTypeName + "].\n"); LDIFOUT.write("# DN:[" + cDN + "]\n"); if (coldDN != null) { LDIFOUT.write("# OLD DN:[" + coldDN + "]\n"); } LDIFOUT.write("\n"); // ***************************************** // Parse the Destination Entry DN to be sure // we have any Quotes.... idxParseDN Naming_Source = new idxParseDN(cDN); cDN = Naming_Source.getDNwithQuotes(); // ***************************************** // Obtain the Namespace. String NameSpace = null; try { NameSpace = IRRSource.irrctx.getNameInNamespace(); if (NameSpace.equals("")) { NameSpace = cDN; } } catch (Exception e) { NameSpace = cDN; } // End of exception // ***************************************** // Now obtain the Source Entry. // try { LP_ENTRY_SEARCH.Start(); NamingEnumeration sl = IRRSource.irrctx.search(cDN, SearchFilter, OS_ctls); LP_ENTRY_SEARCH.Stop(); if (LP_ENTRY_SEARCH.getCurrentDuration() > 1000) { System.out.println(MP + CurrentTimeStamp.getFTS() + " Warning ** Entry Search took " + LP_ENTRY_SEARCH.getElapsedtoString() + " to Complete to Obtain:[" + cDN + "]"); } // End of If. // ******************************** // If the Result is Null, Ignore. if (sl == null) { continue; } LP_ENTRY_TO_LDIF.Start(); while (sl.hasMore()) { SearchResult si = (SearchResult) sl.next(); DNcount++; String DN = null; if (NameSpace.equals("")) { DN = si.getName(); } else if (si.getName().equals("")) { DN = NameSpace; } else { DN = si.getName() + "," + NameSpace; } // ************************************ // Parse the Current DN. idxParseDN pDN = new idxParseDN(DN); // ****************************************** // Write out the DN. // Do not write out a JNDI Quoted DN. // That is not LDIF Compliant. // if (pDN.isQuoted()) { idxIRROutput.WriteLDIF("dn", pDN.getDN(), LDIFOUT); } else { idxIRROutput.WriteLDIF("dn", DN, LDIFOUT); } // Obtain the entries Attributes. Attributes entryattrs = si.getAttributes(); // Obtain ObjectClass First. Attribute eo = entryattrs.get(ObjectClassName); for (NamingEnumeration eov = eo.getAll(); eov.hasMore(); ) { idxIRROutput.WriteLDIF(eo.getID(), eov.next(), LDIFOUT); } // Obtain Naming Attribute Next. if (!"".equals(pDN.getNamingAttribute())) { Attribute en = entryattrs.get(pDN.getNamingAttribute()); for (NamingEnumeration env = en.getAll(); env.hasMore(); ) { idxIRROutput.WriteLDIF(en.getID(), env.next(), LDIFOUT); } } // End of Naming Attribute. // Finish Obtaining remaining Attributes, // in no special sequence. for (NamingEnumeration ea = entryattrs.getAll(); ea.hasMore(); ) { Attribute attr = (Attribute) ea.next(); if ((!ObjectClassName.equalsIgnoreCase(attr.getID())) && (!pDN.getNamingAttribute().equalsIgnoreCase(attr.getID()))) { for (NamingEnumeration ev = attr.getAll(); ev.hasMore(); ) { idxIRROutput.WriteLDIF(attr.getID(), ev.next(), LDIFOUT); } } // End of If } // End of Outer For Loop idxIRROutput.WriteLDIF("", "", LDIFOUT); LP_ENTRY_TO_LDIF.Stop(); if (LP_ENTRY_TO_LDIF.getCurrentDuration() > 1000) { System.out.println(MP + CurrentTimeStamp.getFTS() + "Warning ** Entry to LDIF took " + LP_ENTRY_TO_LDIF.getElapsedtoString() + " to Complete for:[" + DN + "]"); } } // End of Inner While Loop } catch (NameNotFoundException nfe) { if (VERBOSE) { System.out.println(MP + CurrentTimeStamp.getFTS() + " IRR Name Not Found for DN:[" + cDN + "], Ignoring."); } } catch (Exception e) { System.err.println(MP + "IRR Exception on IRRChangeLogger, Obtaining Source Entry, " + e); e.printStackTrace(); ChangeStatus.setError(EXIT_IRR_BACKUP_FAILURE); return; // End Thread. } // End of exception } // End of Outer While Loop. } catch (Exception e) { System.err.println(MP + "IRR Exception on IRRChangeLogger, Obtaining Data From Thread Pipe, " + e); e.printStackTrace(); return; // End Thread. } // End of exception // *************************************** // Show number of entries backed up. try { CloseLDIFOutput(LDIFOUT, OUTPUT_DIRECTORY, OutputCycle, DNcount); } catch (Exception e) { System.err.println(MP + "Exception Closing Collection LDIF Output File. " + e); ChangeStatus.setError(EXIT_GENERIC_FAILURE); return; // End Thread. } // End of exception // *************************************** // Close up Shop. System.out.println(MP + "Closing Directory Context."); try { IRRSource.close(); } catch (Exception e) { System.err.println(e); ChangeStatus.setError(EXIT_IRR_CLOSE_FAILURE); return; // End Thread. } // End of exception // *************************************** // Show the Lap Timings. System.out.println(MP + CurrentTimeStamp.getFTS() + " Final Lap Time for Entry Search: " + LP_ENTRY_SEARCH); System.out.println(MP + CurrentTimeStamp.getFTS() + " Final Lap Time for Entry to LDIF Output: " + LP_ENTRY_TO_LDIF); System.out.println(MP + CurrentTimeStamp.getFTS() + " Final Lap Time for Pipe Communication: " + LP_ENTRY_FROM_PIPE); System.out.println(MP + CurrentTimeStamp.getFTS() + " Final Lap Time for XML to Object: " + LP_XML_TO_OBJECT); // *************************************** // Done. return; } // End of run. /** * OpenLDIFOutput * Method to Prep the LDIF Output File for current Cycle. */ public BufferedWriter OpenLDIFOutput(String _OUTPUT_DIRECTORY, int _Cycle, int _OBUFSIZE) throws IOException, Exception { // *********************************************** // Initialize CurrentActiveFileName = obtainCurrentOutputFileName(_OUTPUT_DIRECTORY); // **************************************** // Determine if the file already exists.... // If it does, that is not good.... File cFile = new File(CurrentActiveFileName); if (cFile.exists()) { System.out.println(MP + CurrentTimeStamp.getFTS() + " New Current Active File:[" + CurrentActiveFileName + "], already exists, possible duplicate process running ending this process."); // ******************************** // Throw Exception. throw new Exception("Error ** New Current Active File Already Exists, " + " possible duplicate process running ending this process."); } // End of If. // ************************************** // Open up the File Output Stream. System.out.println(MP + CurrentTimeStamp.getFTS() + " Preparing new Current Incremental Change Output File:[" + CurrentActiveFileName + "]."); BufferedWriter _LDIFOUT = null; if (_OBUFSIZE <= 0) { _OBUFSIZE = 131072; } // 128KB Buffer. _LDIFOUT = new BufferedWriter(new FileWriter(CurrentActiveFileName, false), _OBUFSIZE); _LDIFOUT.write("version: 1\n"); _LDIFOUT.write("# ***********************************************\n"); _LDIFOUT.write("# FRAMEWORK IRR Directory LDIF Incremental Change.\n"); _LDIFOUT.write("# Source Host: [" + IRRHost + "]\n"); _LDIFOUT.write("# Start Time: " + CurrentTimeStamp.get() + "\n"); _LDIFOUT.write("# ***********************************************\n"); _LDIFOUT.write("\n"); return (_LDIFOUT); } // End of OpenLDIFOutput Method. /** * CloseLDIFOutput * Method to Close the LDIF Output File for current Cycle. */ public void CloseLDIFOutput(BufferedWriter _LDIFOUT, String _OUTPUT_DIRECTORY, int _Cycle, int _DNcount) throws IOException, Exception { // *************************************** // Show number of entries backed up. _LDIFOUT.write("# ***********************************************\n"); _LDIFOUT.write("# End of IRR Incremental Changes\n"); _LDIFOUT.write("# End Time: " + CurrentTimeStamp.get() + "\n"); _LDIFOUT.write("# Entries Contained in LDIF Collection:[" + _DNcount + "]\n"); _LDIFOUT.write("# ***********************************************\n"); _LDIFOUT.write("\n"); System.out.println(MP + CurrentTimeStamp.getFTS() + " Closing out Cycle[" + _Cycle + "] " + "Incremental Changes Logged:[" + _DNcount + "]."); // *************************************** // Close our Output File. _LDIFOUT.flush(); _LDIFOUT.close(); // *********************************************** // Now Rename the Existing Current to a new // TimeStamped Filename. // // Renaming is critical for the LogRestoreDriver // to find the correct files in Sequence to // Play back. // See <CODE>IRRChangeLogRestoreDriver</CODE> for // information regarding processing this file. // String fCycle = new Integer(_Cycle).toString(); while (fCycle.length() < 12) { fCycle = "0" + fCycle; } String NEW = OUTPUT_DIRECTORY + System.getProperty("file.separator") + "IRRCHGLOG_" + CurrentTimeStamp.get().substring(0, 12) + "_" + fCycle + ".ldif"; File cFile = new File(CurrentActiveFileName); File nFile = new File(NEW); if (!cFile.renameTo(nFile)) { System.out.println(MP + CurrentTimeStamp.getFTS() + "Error ** Unable to Rename Current To New Archive " + " LDIF Incremental Change File."); throw new Exception("Error ** Unable to Rename Current To New Archive " + " LDIF Incremental Change File."); } // End of If. } // End of CloseLDIFOutput Method. /** * obtainCurrentOutputFileName * Method to obtain a new current OutputFileName */ public String obtainCurrentOutputFileName(String _OUTPUT_DIRECTORY) { // *********************************************** // Initialize String CURRENT = _OUTPUT_DIRECTORY + System.getProperty("file.separator") + "IRRCHGLOG_CURRENT_" + CurrentTimeStamp.get().substring(0, 12) + ".ldif"; // ***************************** // Return new Filename. return (CURRENT); } // End of obtainCurrentOutputFileName Method. } // End of Class IRRChangeCollector