Beispiel #1
0
        /// <summary>
        /// Attempts to delete files/directories given in the list of DirectoryEntryInfo items to prune.
        /// <para/>returns the number of items that were successfully deleted.
        /// </summary>
        /// <param name="pruneItemList">Gives the list of DirectoryEntryInfo items that are to be removed from the file system.</param>
        /// <param name="deleteEmitter">Gives the IMesgEmitter that will recieve messages about the successfull deletions</param>
        /// <param name="issueEmitter">Gives the IMesgEmitter that will receive any messages about failures while attempting to delete each item.</param>
        /// <param name="reason">Gives the string description for the reason that these items are to be deleted.  Null or Empty will be replaced with [NoReasonGiven]</param>
        /// <returns>The number of items that were successfully deleted.</returns>
        public int DeletePrunedItems(List <DirectoryEntryInfo> pruneItemList, Logging.IMesgEmitter deleteEmitter, Logging.IMesgEmitter issueEmitter, string reason = null)
        {
            int deletedItemCount = 0;           // actually the count of the number of items that we have attempted to delete

            reason = reason.MapNullOrEmptyTo("[NoReasonGiven]");

            for (int idx = 0; idx < pruneItemList.Count; idx++)
            {
                DirectoryEntryInfo entryToDelete = pruneItemList[idx];

                double ageInDays = entryToDelete.CreationAge.TotalDays;

                if (entryToDelete.IsFile)
                {
                    try
                    {
                        System.IO.File.Delete(entryToDelete.Path);
                        deletedItemCount++;
                        deleteEmitter.Emit("Pruned file:'{0}', size:{1}, age:{2:f6} days, reason:{3}", entryToDelete.Path, entryToDelete.Length, ageInDays, reason);
                    }
                    catch (System.Exception ex)
                    {
                        issueEmitter.Emit("Prune failed to delete file:'{0}', error:'{1}'", entryToDelete.Path, ex.Message);
                    }
                }
                else if (entryToDelete.IsDirectory)
                {
                    try
                    {
                        System.IO.Directory.Delete(entryToDelete.Path);
                        deletedItemCount++;
                        deleteEmitter.Emit("Pruned directory:'{0}', size:{1}, age:{2:f6} days, reason:{3}", entryToDelete.Path, entryToDelete.Length, ageInDays, reason);
                    }
                    catch (System.Exception ex)
                    {
                        issueEmitter.Emit("Prune failed to delete directory:'{0}', error:'{1}'", entryToDelete.Path, ex.Message);
                    }
                }
                else
                {
                    issueEmitter.Emit("Prune cannot delete unknown tree node at path:'{0}', reason:{1}", entryToDelete.Path, reason);
                }
            }

            return(deletedItemCount);
        }
        /// <summary>
        /// Inner method used to implement the setup operation.  
        /// </summary>
        protected void InnerSetup(Config config)
        {
            // if needed, clear the prior state.
            if (setupPerformed)
                Clear();

            // record the given configuration
            this.config = config;
            excludedFileSet = new System.Collections.Specialized.StringCollection();
            excludedFileSet.AddRange(config.excludeFileNamesSet.ToArray());

            string dirPath = config.dirPath;

            // try to add a DirectoryEntryInfo record for each of the files that are in the directory
            try
            {
                DirectoryEntryInfo basePathInfo = new DirectoryEntryInfo(dirPath);

                if (basePathInfo.Exists)
                {
                    if (basePathInfo.IsFile)
                        throw new SetupFailureException(Utils.Fcns.CheckedFormat("target path '{0}' does not specify a directory.", dirPath));
                }
                else
                {
                    if (config.createDirectoryIfNeeded)
                        System.IO.Directory.CreateDirectory(dirPath);
                    else
                        throw new SetupFailureException(Utils.Fcns.CheckedFormat("target path '{0}' does not exist.", dirPath));
                }

                // directory exists or has been created - now scan it and record each of the entries that are found therein
                DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
                FileSystemInfo [] directoryFSIArray = dirInfo.GetFileSystemInfos();

                foreach (FileSystemInfo fsi in directoryFSIArray)
                {
                    string path = fsi.FullName;
                    string name = fsi.Name;

                    if (!excludedFileSet.Contains(name) && !excludedFileSet.Contains(path))
                        AddDirEntry(path, true);
                }

                if (numBadDirEntries != 0)
                    logger.Error.Emit("Setup Failure: There are bad directory entries in dir '{0}'", dirPath);
            }
            catch (SetupFailureException sfe)
            {
                SetSetupFaultCode(sfe.Message);
            }
            catch (System.Exception ex)
            {
                SetSetupFaultCode(Utils.Fcns.CheckedFormat("Setup Failure: encountered unexpected exception '{0}' while processing dir '{1}'", ex.Message, dirPath));
            }

            if (!SetupFailed)
            {
                // perform an additional set of tests
                if (string.IsNullOrEmpty(config.fileNamePrefix) || string.IsNullOrEmpty(config.fileNameSuffix))
                    SetSetupFaultCode("Setup Failure: Invalid file name fields in configuration");
                else if (config.advanceRules.fileAgeLimitInSec < 0.0)
                    SetSetupFaultCode("Setup Failure: Config: advanceRules.fileAgeLimitInSec is negative");
                else if (config.purgeRules.dirNumFilesLimit > 0 && config.purgeRules.dirNumFilesLimit < ConfigPurgeNumFilesMinValue)
                    SetSetupFaultCode("Setup Failure: Config: purgeRules.dirNumFilesLimit is too small");
                else if (config.purgeRules.dirNumFilesLimit > 0 && config.purgeRules.dirNumFilesLimit > ConfigPurgeNumFilesMaxValue)
                    SetSetupFaultCode("Setup Failure: Config: purgeRules.dirNumFilesLimit is too large");
                else if (config.purgeRules.dirTotalSizeLimit < 0)
                    SetSetupFaultCode("Setup Failure: Config: purgeRules.dirTotalSizeLimit is negative");
                else if (config.purgeRules.fileAgeLimitInSec < 0.0)
                    SetSetupFaultCode("Setup Failure: Config: purgeRules.maxFileAgeLimitInSec is negative");
            }

            DirectoryEntryInfo activeFileInfo = new DirectoryEntryInfo();

            if (!SetupFailed)
            {
                switch (config.fileNamePattern)
                {
                case FileNamePattern.ByDate:				numFileNumberDigits = 0; break;
                case FileNamePattern.Numeric2DecimalDigits:	numFileNumberDigits = 2; maxFileNumber = 100; break;
                case FileNamePattern.Numeric3DecimalDigits:	numFileNumberDigits = 3; maxFileNumber = 1000; break;
                case FileNamePattern.Numeric4DecimalDigits:	numFileNumberDigits = 4; maxFileNumber = 10000; break;
                default: SetSetupFaultCode("Setup Failure: Invalid file name pattern in configuration"); break;
                }

                // go through the directory file info entries (acquired above) from newest to oldest
                //	and retain the newest valid file that matches the name pattern for this content
                //	manager.  This file will become the initial active file.

                activeFileEntryID = DirEntryID_Invalid;
                activeFileNumber = 0;
                bool matchFound = false;

                IList<Int64> itemKeys = dirEntryIDListSortedByCreatedFTimeUtc.Keys;
                IList<List<int>> itemValues = dirEntryIDListSortedByCreatedFTimeUtc.Values;

                for (int idx = dirEntryIDListSortedByCreatedFTimeUtc.Count - 1; !matchFound && idx >= 0; idx--)
                {
                    Int64 itemFTime = itemKeys[idx];
                    List<int> itemEntryIDList = itemValues[idx];

                    foreach (int itemEntryID in itemEntryIDList)
                    {
                        activeFileEntryID = itemEntryID;

                        if (IsDirEntryIDValid(activeFileEntryID))
                            activeFileInfo = dirEntryList[activeFileEntryID];
                        else
                        {
                            activeFileInfo.Clear();
                            Utils.Asserts.TakeBreakpointAfterFault("Setup: entry ID in ListSortedByCreated is not valid");
                            continue;
                        }

                        // verify that the entry is a file
                        if (!activeFileInfo.IsFile)
                        {
                            Utils.Asserts.TakeBreakpointAfterFault("Setup: entry ID in ListSortedByCreated is not a file");
                            continue;
                        }

                        // divide the name into prefix, middle and suffix fields

                        string fileName = activeFileInfo.Name;
                        bool fileNameIsValidMatch = true;

                        int fileNamePrefixLen = config.fileNamePrefix.Length;
                        int fileNameSuffixLen = config.fileNameSuffix.Length;

                        int split1Idx = Math.Min(fileNamePrefixLen, fileName.Length);   // prevent attempting to call Substring with a second arg that is beyond the end of the string.
                        string prefix = fileName.Substring(0, split1Idx);
                        string rest = fileName.Substring(split1Idx);
                        int restLen = rest.Length;

                        string middle = string.Empty, suffix = string.Empty;

                        if (restLen >= fileNameSuffixLen)
                        {
                            int splitPoint = restLen - fileNameSuffixLen;

                            middle = rest.Substring(0, splitPoint);
                            suffix = rest.Substring(splitPoint);
                        }
                        else
                        {
                            // this file name does not match requirements - exclude from search for current active file
                            fileNameIsValidMatch = false;
                        }

                        // test if the prefix and suffix's match
                        if (prefix != config.fileNamePrefix || suffix != config.fileNameSuffix)
                            fileNameIsValidMatch = false;

                        // test if the middle is valid
                        if (numFileNumberDigits > 0)
                        {
                            int testFileNumber = -1;
                            bool match = int.TryParse(middle, out testFileNumber);

                            if (testFileNumber >= 0 && middle.Length == numFileNumberDigits && match)
                                activeFileNumber = testFileNumber;
                            else
                                fileNameIsValidMatch = false;
                        }
                        else
                        {
                            // for FileNamePattern.ByDate files, we assume that the middle is valid if it is not empty
                            if (middle.Length == 0)
                                fileNameIsValidMatch = false;
                        }

                        matchFound = fileNameIsValidMatch;
                        if (matchFound)
                            break;
                    }
                }

                if (!matchFound && dirEntryIDListSortedByCreatedFTimeUtc.Count != 0)
                    logger.Warning.Emit("Setup Warning: no valid active file found in non-empty directory '{0}'", dirPath);
            }

            if (!SetupFailed && config.enableAutomaticCleanup)
            {
                for (int limit = 0; IsDirectoryCleanupNeeded && (limit < config.maxAutoCleanupDeletes); limit++)
                    PerformIncrementalCleanup();
            }

            if (SetupFailed)
            {
                logger.Error.Emit("Directory is not usable: path:'{0}' fault:'{1}'", dirPath, setupFaultCode);
            }
            else
            {
                logger.Debug.Emit("Directory is usable: path:'{0}' number of files:{1} active file:'{2}'",
                                            dirPath, dirEntryIDListSortedByName.Count, activeFileInfo.Name);
            }

            setupPerformed = true;
        }
        /// <summary>
        /// Internal method that is used to generate the next Active file name.  May delete the oldest file in order to reuse the name when appropriate.
        /// </summary>
        protected void GenerateNextActiveFile()
        {
            // any current active entry is no longer active
            activeFileEntryID = DirEntryID_Invalid;

            // increment the file number (for numeric files)
            activeFileNumber += 1;
            if (activeFileNumber >= maxFileNumber)
                activeFileNumber = 0;

            string formatStr = string.Empty;
            string middleStr = string.Empty;

            switch (config.fileNamePattern)
            {
            default:
            case FileNamePattern.ByDate:
                {
                    DateTime dtNow = DateTime.Now;

                    middleStr = Utils.Dates.CvtToString(ref dtNow, Utils.Dates.DateTimeFormat.ShortWithMSec);
                }
                break;

            case FileNamePattern.Numeric2DecimalDigits:
            case FileNamePattern.Numeric3DecimalDigits:
            case FileNamePattern.Numeric4DecimalDigits:
                {
                    formatStr = Utils.Fcns.CheckedFormat("D{0}", numFileNumberDigits);
                    middleStr = activeFileNumber.ToString(formatStr);
                }
                break;
            }

            string name = config.fileNamePrefix + middleStr + config.fileNameSuffix;

            // we have a full path now.
            int entryID = FindDirEntryByFileName(name);

            bool nameWasInMap = (IsDirEntryIDValid(entryID) ? RemoveDirEntry(entryID) : false);

            // if the file is already known
            string filePath = System.IO.Path.Combine(config.dirPath, name);
            DirectoryEntryInfo entryInfo = new DirectoryEntryInfo(filePath);
            FileSystemInfo entryFSI = entryInfo.FileSystemInfo;

            Utils.Asserts.LogIfConditionIsNotTrue((nameWasInMap == entryInfo.Exists), "GenerateNextActiveFile: name already exists only if it was removed from map");

            if (entryInfo.Exists)
            {
                // delete the file (failure means we cannot use this name...)

                double fileAgeInHours = entryInfo.CreationAge.TotalHours;
                Int64 fileSize = entryInfo.Length;

                logger.Trace.Emit("GenerateNextActiveFile: Attempting to delete prior file:'{0}' size:{1} age:{2} hours", entryInfo.Name, fileSize, fileAgeInHours.ToString("f3"));

                try {
                    entryFSI.Delete();
                    logger.Debug.Emit("GenerateNextActiveFile: Deleted prior file:'{0}' size:{1} age:{2} hours", entryInfo.Name, fileSize, fileAgeInHours.ToString("f3"));
                }
                catch (System.Exception ex)
                {
                    logger.Error.Emit("GenerateNextActiveFile: failed to delete prior file:'{1}', error:'{1}'", entryInfo.Name, ex.Message);
                    return;
                }
            }

            // the name has been generated and if it existed, it has been deleted
            //	create the entry, add it to the maps and set the active entry to refer to the new entry

            activeFileEntryID = AddDirEntry(entryInfo.Path, false);

            DirectoryEntryInfo activeFileEntry = dirEntryList[activeFileEntryID];

            logger.Debug.Emit("GenerateNextActiveFile active file is now:'{0}' id:{1}", activeFileEntry.Name, activeFileEntryID);
        }