From 9778c5adbeceb5042af7ccfe048c90b207b47501 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 5 Feb 2018 08:36:50 +0100 Subject: [PATCH] Use EnhancedFileSystemWatcher.cs instead of NuGet --- .../Util/EnhancedFileSystemWatcher.cs | 265 ++++++++++++++++++ src/WireMock.Net/WireMock.Net.csproj | 1 - 2 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs diff --git a/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs b/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs new file mode 100644 index 00000000..c2139cbf --- /dev/null +++ b/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using JetBrains.Annotations; +using WireMock.Validation; + +namespace WireMock.Util +{ + /// + /// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file. + /// + /// + /// + public class EnhancedFileSystemWatcher : FileSystemWatcher, IDisposable + { + #region Private Members + // Default Watch Interval in Milliseconds + private const int DefaultWatchInterval = 100; + + // This Dictionary keeps the track of when an event occured last for a particular file + private ConcurrentDictionary _lastFileEvent; + + // Watch Interval in Milliseconds + private int _interval; + + // Timespan created when interval is set + private TimeSpan _recentTimeSpan; + #endregion + + #region Public Properties + /// + /// Interval, in milliseconds, within which events are considered "recent". + /// + [PublicAPI] + public int Interval + { + get => _interval; + set + { + _interval = value; + + // Set timespan based on the value passed + _recentTimeSpan = new TimeSpan(0, 0, 0, 0, value); + } + } + + /// + /// Allows user to set whether to filter recent events. + /// If this is set a false, this class behaves like System.IO.FileSystemWatcher class. + /// + [PublicAPI] + public bool FilterRecentEvents { get; set; } + #endregion + + #region Constructors + /// + /// Initializes a new instance of the class. + /// + /// The interval. + public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval) + { + Check.Condition(interval, i => i >= 0, nameof(interval)); + + InitializeMembers(interval); + } + + /// + /// Initializes a new instance of the class. + /// + /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. + /// The interval. + public EnhancedFileSystemWatcher([NotNull] string path, int interval = DefaultWatchInterval) : base(path) + { + Check.NotNullOrEmpty(path, nameof(path)); + Check.Condition(interval, i => i >= 0, nameof(interval)); + + InitializeMembers(interval); + } + + /// + /// Initializes a new instance of the class. + /// + /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. + /// The type of files to watch. For example, "*.txt" watches for changes to all text files. + /// The interval. + public EnhancedFileSystemWatcher([NotNull] string path, [NotNull] string filter, int interval = DefaultWatchInterval) : base(path, filter) + { + Check.NotNullOrEmpty(path, nameof(path)); + Check.NotNullOrEmpty(filter, nameof(filter)); + Check.Condition(interval, i => i >= 0, nameof(interval)); + + InitializeMembers(interval); + } + #endregion + + #region Events + // These events hide the events from the base class. + // We want to raise these events appropriately and we do not want the + // users of this class subscribing to these events of the base class accidentally + + /// + /// Occurs when a file or directory in the specified is changed. + /// + public new event FileSystemEventHandler Changed; + + /// + /// Occurs when a file or directory in the specified is created. + /// + public new event FileSystemEventHandler Created; + + /// + /// Occurs when a file or directory in the specified is deleted. + /// + public new event FileSystemEventHandler Deleted; + + /// + /// Occurs when a file or directory in the specified is renamed. + /// + public new event RenamedEventHandler Renamed; + #endregion + + #region Protected Methods to raise the Events for this class + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnChanged(FileSystemEventArgs e) + { + Changed?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnCreated(FileSystemEventArgs e) + { + Created?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnDeleted(FileSystemEventArgs e) + { + Deleted?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnRenamed(RenamedEventArgs e) + { + Renamed?.Invoke(this, e); + } + #endregion + + #region Private Methods + /// + /// This Method Initializes the private members. + /// Interval is set to its default value of 100 millisecond. + /// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized. + /// We subscribe to the base class events. + /// + private void InitializeMembers(int interval = 100) + { + Interval = interval; + FilterRecentEvents = true; + _lastFileEvent = new ConcurrentDictionary(); + + base.Created += OnCreated; + base.Changed += OnChanged; + base.Deleted += OnDeleted; + base.Renamed += OnRenamed; + } + + /// + /// This method searches the dictionary to find out when the last event occured + /// for a particular file. If that event occured within the specified timespan + /// it returns true, else false + /// + /// The filename to be checked + /// True if an event has occured within the specified interval, False otherwise + private bool HasAnotherFileEventOccuredRecently(string fileName) + { + // Check dictionary only if user wants to filter recent events otherwise return value stays false. + if (!FilterRecentEvents) + { + return false; + } + + bool retVal = false; + if (_lastFileEvent.ContainsKey(fileName)) + { + // If dictionary contains the filename, check how much time has elapsed + // since the last event occured. If the timespan is less that the + // specified interval, set return value to true + // and store current datetime in dictionary for this file + DateTime lastEventTime = _lastFileEvent[fileName]; + DateTime currentTime = DateTime.Now; + TimeSpan timeSinceLastEvent = currentTime - lastEventTime; + retVal = timeSinceLastEvent < _recentTimeSpan; + _lastFileEvent[fileName] = currentTime; + } + else + { + // If dictionary does not contain the filename, + // no event has occured in past for this file, so set return value to false + // and append filename along with current datetime to the dictionary + _lastFileEvent.TryAdd(fileName, DateTime.Now); + } + + return retVal; + } + + #region FileSystemWatcher EventHandlers + // Base class Event Handlers. Check if an event has occured recently and call method + // to raise appropriate event only if no recent event is detected + private void OnChanged(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccuredRecently(e.FullPath)) + { + OnChanged(e); + } + } + + private void OnCreated(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccuredRecently(e.FullPath)) + { + OnCreated(e); + } + } + + private void OnDeleted(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccuredRecently(e.FullPath)) + { + OnDeleted(e); + } + } + + private void OnRenamed(object sender, RenamedEventArgs e) + { + if (!HasAnotherFileEventOccuredRecently(e.OldFullPath)) + { + OnRenamed(e); + } + } + #endregion + #endregion + + #region IDisposable Members + /// + /// Releases all resources used by the . + /// + public new void Dispose() + { + base.Dispose(); + } + #endregion + } +} \ No newline at end of file diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 0e828b45..af1d6660 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -41,7 +41,6 @@ -