package jeffaschenk.commons.frameworks.cnxidx.admin; import jeffaschenk.commons.frameworks.cnxidx.utility.StopWatch; import jeffaschenk.commons.frameworks.cnxidx.utility.ldap.*; import jeffaschenk.commons.touchpoint.model.threads.CircularObjectStack; import java.io.*; import javax.naming.*; import javax.naming.directory.*; /** * IRR Backup Output Thread. Will Read Directory Entry and * backup the data to an LDIF compilent Data file. * Backup output conforms to the LDIF Specification: RFC2849. * @author jeff.schenk * @version 4.4 $Revision * Developed 2005 */ /** * IRRBackupOutputThread * Class to run Output Thread. */ class IRRBackupOutputThread implements Runnable, idxCMDReturnCodes { /** * IRRBackupOutputThread * Class to provide Output Thread to Write LDIF from Entries obtain via an * Object Stack. */ Thread t; private CircularObjectStack cosin; private IRRBackupStatusNew WriterStatus; private static String MP = "IRRBackupOutputThread: "; private static String ENTRY_SOURCE_DN = null; private static String OUTPUT_FILENAME = null; private static int DN_SEGMENTATION_OUTPUT = 0; private static boolean VERBOSE = false; private static boolean DEBUG = false; private static boolean APPEND = false; private static boolean NICE = true; private static int OBUFSIZE = 0; private idxTimeStamp CurrentTimeStamp = new idxTimeStamp(); private boolean ExitOnException = false; /** * IRRBackupOutputThread Contructor class driven. * * @param cosin Circular Object Stack for placing DN's on Read Queue. * @param WriterStatus Object used for Thread Status. * @param ENTRY_SOURCE_DN Specified where to begin Backup in Tree. * @param OUTPUT_FILENAME Destination Output Filename. * @param OBUFSIZE BufferedWriter Output Buffer Size. * @param APPEND Indicate if Output Should be Appended. * @param NICE Indicate if Output Should be nicely formatted. * @param DN_SEGMENTATION_OUTPUT Indicates if Output should be segmented by DN. * @param VERBOSE Indicate Verbosity. * @param DEBUG Indicate DEBUGGING. * @param ExitOnException Indicate Exit on Exceptions. */ IRRBackupOutputThread(CircularObjectStack cosin, IRRBackupStatusNew WriterStatus, String ENTRY_SOURCE_DN, String OUTPUT_FILENAME, int OBUFSIZE, boolean APPEND, boolean NICE, int DN_SEGMENTATION_OUTPUT, boolean VERBOSE, boolean DEBUG, boolean ExitOnException) { // **************************************** // Set My Incoming Parameters. this.cosin = cosin; this.ENTRY_SOURCE_DN = ENTRY_SOURCE_DN; this.OUTPUT_FILENAME = OUTPUT_FILENAME; this.OBUFSIZE = OBUFSIZE; this.APPEND = APPEND; this.NICE = NICE; this.DN_SEGMENTATION_OUTPUT = DN_SEGMENTATION_OUTPUT; this.VERBOSE = VERBOSE; this.DEBUG = DEBUG; this.ExitOnException = ExitOnException; this.WriterStatus = WriterStatus; // **************************************** // Ready the Synchronized Object and start // the Thread. t = new Thread(this, "IRRbackup_LDIFWriter"); t.start(); // Start the Thread } // End of Contructor. /** * run * Thread to Output LDIF Backup Image. */ public void run() { // *********************************************** // Initialize our StopWatch to measure Duration // of Thread. StopWatch sw = new StopWatch(); sw.start(); // *********************************************** // Initialize Thread Variables. Object ZEN = null; IRRBackupSearchResult searchresultwrapper = null; long DNcount = 0; long SEGMENT_DNcount = 0; long SEGMENT = 0; long memfree; String tname = Thread.currentThread().getName(); System.out.println(MP + "LDIF Output Thread Established for:[" + tname + "]"); // *********************************************** // Initialize my LAP Timers idxLapTime LP_ENTRY_TO_LDIF = new idxLapTime(); idxLapTime LP_ENTRY_FROM_COS = new idxLapTime(); // ************************************************** // Obtain Runtime Object. Runtime rt = Runtime.getRuntime(); // ****************************************** // Prepare to Open up the File Output Stream. System.out.println(MP + "Preparing Backup Output File..."); if (OBUFSIZE <= 0) { OBUFSIZE = 131072; } // 128KB Buffer. System.out.println(MP + "Backup Output File Buffer Size:[" + OBUFSIZE + "]"); // ******************************************** // Open Output Stream and Initialize. BufferedWriter LDIFOUT = null; try { LDIFOUT = this.openOutput(OUTPUT_FILENAME, APPEND, OBUFSIZE, SEGMENT); } catch (Exception e) { System.err.println(MP + "Exception opening Backup LDIF Output File. " + e); WriterStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); return; // End Thread. } // End of exception // ************************************** // Loop to process commands from Walker // Thread. try { while (true) { searchresultwrapper = null; ZEN = null; LP_ENTRY_FROM_COS.Start(); if (cosin.hasMoreNodes()) { ZEN = cosin.getNext(); } LP_ENTRY_FROM_COS.Stop(); // *************************** // Did anything get pulled // from stack? if (ZEN == null) { t.sleep(1000); continue; } // End of Nothing in Stack yet to Process. // *************************** // Should We End Thread? if (ZEN instanceof String) { // *************************** // Should We End Thread? if (IRRBackupNew.END_OF_DATA.equals((String) ZEN)) { break; } // ***************************** // Is the Entry a LDIF Comment? // Or a Simple NewLine? if ((((String) ZEN).startsWith("#")) || (((String) ZEN).equals("\n"))) { try { LDIFOUT.write((String) ZEN); continue; } catch (Exception e) { System.err.println(MP + "Exception Writing to Backup LDIF Output File. " + e); WriterStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); return; // End Thread. } // End of exception } // End of If. } // End of Check for String. else if (!(ZEN instanceof IRRBackupSearchResult)) { continue; } // *********************************** // Ok, this must be a SearchResult, so // Process. // searchresultwrapper = (IRRBackupSearchResult) ZEN; // Cast to Appropreiate Object. if (searchresultwrapper == null) { continue; } LP_ENTRY_TO_LDIF.Start(); String DN = searchresultwrapper.searchresult.getName(); if ((DN == null) || (DN.trim().equalsIgnoreCase(""))) { DN = searchresultwrapper.DistinguishedName; } // ****************************************** // Write out the DN. // Do not write out a JNDI Quoted DN. // That is not LDIF Compliant. // idxParseDN pDN = new idxParseDN(DN); DNcount++; if (NICE) { LDIFOUT.write("dn: "); if (pDN.isQuoted()) { LDIFOUT.write(pDN.getDN()); } else { LDIFOUT.write(DN); } LDIFOUT.write("\n"); } else { if (pDN.isQuoted()) { idxIRROutput.WriteLDIF("dn", pDN.getDN(), LDIFOUT); } else { idxIRROutput.WriteLDIF("dn", DN, LDIFOUT); } } // End of DN Output. // Obtain the entries Attributes. Attributes entryattrs = searchresultwrapper.searchresult.getAttributes(); // Obtain ObjectClass First. Attribute eo = entryattrs.get(IRRBackupReaderThread.ObjectClassName); for (NamingEnumeration eov = eo.getAll(); eov.hasMore(); ) { idxIRROutput.WriteLDIF(eo.getID(), eov.next(), LDIFOUT); } // Obtain Naming Attribute Next. // One will not exist for an Alias. if (!"".equals(pDN.getNamingAttribute())) { Attribute en = entryattrs.get(pDN.getNamingAttribute()); if (en != null) { for (NamingEnumeration env = en.getAll(); env.hasMore(); ) { idxIRROutput.WriteLDIF(en.getID(), env.next(), LDIFOUT); } } // End of Inner If. } // 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 ((!IRRBackupReaderThread.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 + "Warning ** Entry to LDIF took " + LP_ENTRY_TO_LDIF.getElapsedtoString() + " to Complete for:[" + DN + "]"); } // End of Warning Message due to time spent processing. // ********************************** // Check here to Segment the Output // File based upon Number of // DN Entries. if (DN_SEGMENTATION_OUTPUT > 0) { SEGMENT_DNcount++; if (SEGMENT_DNcount >= DN_SEGMENTATION_OUTPUT) { // ****************************** // Close off our Existing // Output File. this.closeOutput(LDIFOUT, SEGMENT_DNcount, SEGMENT, -1); // ****************************** // Prepare a new FileName. SEGMENT++; SEGMENT_DNcount = 0; LDIFOUT = this.openOutput(OUTPUT_FILENAME, APPEND, OBUFSIZE, SEGMENT); } // End of Check for Making an Output Segmentation. } // End of Segmentation Check. } // End of Outer While Loop. } catch (Exception e) { System.err.println(MP + "IRR Exception on IRRbackup, Performing Output Data Loop, " + e); return; // End Thread. } // End of exception. // *************************************** // Show number of entries backed up. if (DNcount > 0) { System.out.println(MP + "Successful Backup, Entries Included In Backup:[" + DNcount + "], Output Segments:[" + (SEGMENT + 1) + "]."); } else { System.out.println(MP + "No Entries were Included in Backup:[" + DNcount + "]"); } // End of Else. // *************************************** // Close our Output File/Last Segment. try { this.closeOutput(LDIFOUT, SEGMENT_DNcount, SEGMENT, DNcount); } catch (Exception e) { System.err.println(MP + "Exception closing Output File. " + e); WriterStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_CLOSE_FAILURE); return; // End Thread. } // End of exception // *************************************** // Show the Lap Timings. System.out.println(MP + "Lap Time for Entry to LDIF Output: " + LP_ENTRY_TO_LDIF); System.out.println(MP + "Lap Time for Stack Communication: " + LP_ENTRY_FROM_COS); // *************************************** // Show the Duration of Thread. sw.stop(); System.out.println(MP + "Thread Duration: " + sw.getElapsedTimeString()); // *************************************** // Done. return; } // End of run. /* * Private method to Open up Output Writer. */ private BufferedWriter openOutput(String outputfilename, boolean append, int outputbufsize, long segment) throws Exception { // ********************************** // Prepare Filename. String filename = outputfilename; if (!filename.toLowerCase().endsWith(".ldif")) { filename = filename + ".ldif"; } // ********************************** // Determine if we have a new Segment if (segment > 0) { filename = filename + ".segment_" + this.formatSegment(segment); } // End of Check for Segmentation File Name Setup. // ********************************** // Create a File Object. File outputfile = new File(filename); // ********************************** // Open File. BufferedWriter LDIFOUT = new BufferedWriter( new FileWriter(outputfile, append), outputbufsize); // ********************************** // Create Header Information. if (!APPEND) { LDIFOUT.write("version: 1\n"); } LDIFOUT.write("# ***********************************************\n"); LDIFOUT.write("# FRAMEWORK IRR Directory LDIF Backup.\n"); if (ENTRY_SOURCE_DN.equals("")) { LDIFOUT.write("# Source DN: [" + "ROOT" + "]\n"); } else { LDIFOUT.write("# Source DN: [" + ENTRY_SOURCE_DN + "]\n"); } LDIFOUT.write("# Start Time: " + CurrentTimeStamp.get() + "\n"); if (segment > 0) { LDIFOUT.write("# Backup Segment: [" + this.formatSegment(segment) + "]\n"); } LDIFOUT.write("# ***********************************************\n"); LDIFOUT.write("\n"); // **************************************** // Return BufferedWriter. return LDIFOUT; } // End of openOutput Private Method. /* * Private method to Open up Output Writer. */ private void closeOutput(BufferedWriter LDIFOUT, long dncount, long segment, long finalcount) throws Exception { // *************************************** // Show number of entries backed up. try { LDIFOUT.write("# **************************************************************\n"); LDIFOUT.write("# End of IRR Backup LDIF Segment\n"); LDIFOUT.write("# End Time: " + CurrentTimeStamp.get() + "\n"); if (segment > 0) { LDIFOUT.write("# Segment Number: " + this.formatSegment(segment) + "\n"); LDIFOUT.write("# Entries Contained in this LDIF Backup Segment:[" + dncount + "]\n"); } // End of Check to Show Segment Size. if (finalcount >= 0) { LDIFOUT.write("# Total Entries Contained in All Backup LDIF Segments:[" + finalcount + "]\n"); } // End of Check to Show Final Count. LDIFOUT.write("# **************************************************************\n"); LDIFOUT.write("\n"); } catch (Exception e) { System.err.println(MP + "Exception closing Backup LDIF Output File. " + e); WriterStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_FAILURE); throw e; // Rethrow Exception. } // End of exception // *************************************** // Close our Output File. try { LDIFOUT.flush(); LDIFOUT.close(); } catch (Exception e) { System.err.println(MP + "Exception closing Output File. " + e); WriterStatus.setError(EXIT_IRR_BACKUP_LDIF_OUTPUT_CLOSE_FAILURE); throw e; // Rethrow Exception. } // End of exception } // End of closeOutput Private Method. /* * Private Method to format the Segment Number into a String. */ private String formatSegment(long segment) { String segstr = Long.toString(segment); for (int i = 0; i <= 7 && segstr.length() <= 7; i++) { segstr = "0" + segstr; } return segstr; } // End of formatSegment Private method. } ///:~ End of Class IRRBackupOutputThread