// // HostFileChangeMonitor.cs // // Authors: // Marek Habersack // // Copyright (C) 2010 Novell, Inc. (http://novell.com/) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Runtime.Caching.Hosting; using System.Text; namespace System.Runtime.Caching { public sealed class HostFileChangeMonitor : FileChangeMonitor { const long INVALID_FILE_SIZE = -1L; static readonly object notificationSystemLock = new object (); static readonly DateTime missingFileTimeStamp = new DateTime (0x701CE1722770000); static IFileChangeNotificationSystem notificationSystem; static bool notificationSystemSetFromHost; ReadOnlyCollection filePaths; DateTime lastModified; string uniqueId; bool disposeNotificationSystem; Dictionary notificationStates; public override ReadOnlyCollection FilePaths { get { return filePaths;} } public override DateTimeOffset LastModified { get { return lastModified; } } public override string UniqueId { get { return uniqueId; } } public HostFileChangeMonitor (IList filePaths) { if (filePaths == null) throw new ArgumentNullException ("filePaths"); if (filePaths.Count == 0) throw new ArgumentException ("filePaths contains zero items."); var list = new List (filePaths.Count); var sb = new StringBuilder (); lastModified = DateTime.MinValue; DateTime lastWrite; long size; bool ignoreForUniqueId; foreach (string p in filePaths) { if (String.IsNullOrEmpty (p)) throw new ArgumentException ("A path in the filePaths list is null or an empty string."); if (!Path.IsPathRooted (p)) throw new ArgumentException ("Absolute path information is required."); if (list.Contains (p)) ignoreForUniqueId = true; else ignoreForUniqueId = false; list.Add (p); if (ignoreForUniqueId) continue; if (Directory.Exists (p)) { var di = new DirectoryInfo (p); lastWrite = di.LastWriteTimeUtc; size = INVALID_FILE_SIZE; } else if (File.Exists (p)) { var fi = new FileInfo (p); lastWrite = fi.LastWriteTimeUtc; size = fi.Length; } else { lastWrite = missingFileTimeStamp; size = INVALID_FILE_SIZE; } if (lastWrite > lastModified) lastModified = lastWrite; sb.AppendFormat ("{0}{1:X}{2:X}", p, lastWrite.Ticks, (ulong)size); } this.filePaths = new ReadOnlyCollection (list); this.uniqueId = sb.ToString (); bool initComplete = false; try { InitMonitoring (); initComplete = true; } finally { InitializationComplete (); if (!initComplete) Dispose (); } } void InitMonitoring () { IServiceProvider host; if (!notificationSystemSetFromHost && (host = ObjectCache.Host) != null) { lock (notificationSystemLock) { if (!notificationSystemSetFromHost) { if (host != null) notificationSystem = host.GetService (typeof (IFileChangeNotificationSystem)) as IFileChangeNotificationSystem; if (notificationSystem != null) notificationSystemSetFromHost = true; } } } if (!notificationSystemSetFromHost) { notificationSystem = new FileChangeNotificationSystem (); disposeNotificationSystem = true; } object state; // TODO: what are these two used for? DateTimeOffset lastWriteTime; long fileSize; notificationStates = new Dictionary (filePaths.Count, Helpers.StringEqualityComparer); foreach (string path in filePaths) { if (notificationStates.ContainsKey (path)) continue; // silently ignore dupes notificationSystem.StartMonitoring (path, OnChanged, out state, out lastWriteTime, out fileSize); notificationStates.Add (path, state); } } protected override void Dispose (bool disposing) { if (disposing && notificationSystem != null) { object state; foreach (var de in notificationStates) { state = de.Value; if (state == null) continue; try { notificationSystem.StopMonitoring (de.Key, state); } catch { // ignore } } if (disposeNotificationSystem) { var fcns = notificationSystem as FileChangeNotificationSystem; if (fcns != null) { try { fcns.Dispose (); } finally { notificationSystem = null; } } } } } } }