2007-11-07 Igor Zelmanovich <igorz@mainsoft.com>
[mono.git] / mcs / class / System.Web / System.Web.Caching / CacheDependency.cs
index 7abbd0565a0a821dc75779358ad87b2a3c4739d8..03794c6e89868a9190f09df2f4f5f209dc9c4873 100644 (file)
@@ -1,62 +1,78 @@
-// 
-// System.Web.Caching.CacheDependency
 //
-// Authors:
-//     Patrik Torstensson (Patrik.Torstensson@labs2.com)
-//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// System.Web.Caching.CachedDependency
 //
-// (C) Copyright Patrik Torstensson, 2001
-// (c) 2003 Ximian, Inc. (http://www.ximian.com)
+// Author(s):
+//  Lluis Sanchez (lluis@ximian.com)
+//
+// (C) 2005 Novell, Inc (http://www.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.Web;
+
+using System.Collections;
+using System.IO;
+using System.Security.Permissions;
+#if NET_2_0
+using System.Text;
+#endif
 
 namespace System.Web.Caching
 {
-       internal class CacheDependencyChangedArgs : EventArgs
-       {
-               string key;
-
-               public CacheDependencyChangedArgs (string key)
-               {
-                       this.key = key;
-               }
-
-               public string Key {
-                       get { return key; }
-               }
-       }
-
-       internal delegate void CacheDependencyChangedHandler (object sender, CacheDependencyChangedArgs args);
-
-       public sealed class CacheDependency : IDisposable
-       {
-               static string [] noStrings = new string [0];
-               static CacheDependency noDependency = new CacheDependency ();
+#if NET_2_0
+       // CAS
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public class CacheDependency: IDisposable {
+#else
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public sealed class CacheDependency: IDisposable {
+#endif
+               string[] cachekeys;
+               CacheDependency dependency;
                DateTime start;
-               string [] filenames;
-               bool changed;
-               bool disposed;
-               CacheEntry [] entries;
-               CacheItemRemovedCallback removedDelegate;
-               EventHandler changedDelegate;
-
-               private CacheDependency ()
+               Cache cache;
+               FileSystemWatcher[] watchers;
+               bool hasChanged;
+#if NET_2_0
+               bool used;
+               DateTime utcLastModified;
+#endif
+               object locker = new object ();
+               
+#if NET_2_0
+               public CacheDependency (): this (null, null, null, DateTime.Now)
                {
                }
-
-               public CacheDependency (string filename)
-                       : this (filename, DateTime.MaxValue)
+#endif
+               
+               public CacheDependency (string filename): this (new string[] { filename }, null, null, DateTime.Now)
                {
                }
-
-               public CacheDependency (string filename, DateTime start)
-                       : this (new string [] {filename}, null, null, start)
+               
+               public CacheDependency (string[] filenames): this (filenames, null, null, DateTime.Now)
                {
                }
-
-               public CacheDependency (string [] filenames)
-                       : this (filenames, null, null, DateTime.MaxValue)
+               
+               public CacheDependency (string filename, DateTime start): this (new string[] { filename }, null, null, start)
                {
                }
 
@@ -65,113 +81,216 @@ namespace System.Web.Caching
                {
                }
 
-               public CacheDependency (string [] filenames, string [] cachekeys)
-                       : this (filenames, cachekeys, null, DateTime.MaxValue)
+               public CacheDependency (string[] filenames, string[] cachekeys): this (filenames, cachekeys, null, DateTime.Now)
                {
                }
-
-               public CacheDependency (string [] filenames, string [] cachekeys, DateTime start)
-                       : this (filenames, cachekeys, null, start)
+               
+               public CacheDependency (string[] filenames, string[] cachekeys, CacheDependency dependency): this (filenames, cachekeys, dependency, DateTime.Now)
                {
                }
-
-               public CacheDependency (string [] filenames, string [] cachekeys, CacheDependency dependency)
-                       : this (filenames, cachekeys, dependency, DateTime.MaxValue)
+               
+               public CacheDependency (string[] filenames, string[] cachekeys, DateTime start): this (filenames, cachekeys, null, start)
                {
                }
-
-               public CacheDependency (string [] filenames,
-                                       string [] cachekeys,
-                                       CacheDependency dependency,
-                                       DateTime start)
+               
+               public CacheDependency (string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start)
                {
-                       this.start = start;
-                       if (filenames == null)
-                               this.filenames = noStrings;
-                               
-                       foreach (string file in filenames) {
-                               if (file == null)
-                                       throw new ArgumentNullException ("filenames");
+                       if (filenames != null) {
+                               watchers = new FileSystemWatcher [filenames.Length];
+                               for (int n=0; n<filenames.Length; n++) {
+                                       FileSystemWatcher watcher = new FileSystemWatcher ();
+                                       if (Directory.Exists (filenames [n])) {
+                                               watcher.Path = filenames [n];
+                                       } else {
+                                               string parentPath = Path.GetDirectoryName (filenames [n]);
+                                               if (parentPath != null && Directory.Exists (parentPath)) {
+                                                       watcher.Path = parentPath;
+                                                       watcher.Filter = Path.GetFileName (filenames [n]);
+                                               } else
+                                                       continue;
+                                       }
+                                       watcher.NotifyFilter |= NotifyFilters.Size;
+                                       watcher.Created += new FileSystemEventHandler (OnChanged);
+                                       watcher.Changed += new FileSystemEventHandler (OnChanged);
+                                       watcher.Deleted += new FileSystemEventHandler (OnChanged);
+                                       watcher.Renamed += new RenamedEventHandler (OnChanged);
+                                       watcher.EnableRaisingEvents = true;
+                                       watchers [n] = watcher;
+                               }
                        }
+                       this.cachekeys = cachekeys;
+                       this.dependency = dependency;
+                       if (dependency != null)
+                               dependency.DependencyChanged += new EventHandler (OnChildDependencyChanged);
+                       this.start = start;
 
-                       this.filenames = filenames;
-
-                       if (cachekeys == null)
-                               cachekeys = noStrings;
+#if NET_2_0
+                       FinishInit ();
+#endif
+               }
 
-                       foreach (string ck in cachekeys) {
-                               if (ck == null)
-                                       throw new ArgumentNullException ("cachekeys");
+#if NET_2_0
+               public virtual string GetUniqueID ()
+               {
+                       StringBuilder sb = new StringBuilder ();
+                       lock (locker) {
+                               if (watchers != null)
+                                       foreach (FileSystemWatcher fsw in watchers)
+                                               if (fsw != null && fsw.Path != null && fsw.Path.Length != 0)
+                                                       sb.AppendFormat ("_{0}", fsw.Path);
                        }
 
-                       if (dependency == null)
-                               dependency = noDependency;
-
-
-                       this.changed = dependency.changed;
-                       if (changed == true)
-                               return;
-
-                       int nentries = cachekeys.Length + ((dependency.entries == null) ? 0 :
-                                                                 dependency.entries.Length);
-
-                       if (nentries != 0) {
-                               this.removedDelegate = new CacheItemRemovedCallback (CacheItemRemoved);
-                               this.entries = new CacheEntry [nentries];
-                               
-                               int i = 0;
-                               if (dependency.entries != null) {
-                                       foreach (CacheEntry entry in dependency.entries) {
-                                               entry._onRemoved += removedDelegate;
-                                               entries [i++] = entry;
-                                       }
-                               }
+                       if (cachekeys != null)
+                               foreach (string key in cachekeys)
+                                       sb.AppendFormat ("_{0}", key);
+                       return sb.ToString ();
+               }
+#endif
+               
+               void OnChanged (object sender, FileSystemEventArgs args)
+               {
+                       OnDependencyChanged (sender, args);
+               }
 
-                               Cache cache = HttpRuntime.Cache;
-                               for (int c=0; c<cachekeys.Length; c++) {
-                                       CacheEntry entry = cache.GetEntry (cachekeys [c]);
-                                       entry._onRemoved += removedDelegate;
-                                       entries [i++] = entry;
+               bool DoOnChanged ()
+               {
+                       if (DateTime.Now < start)
+                               return false;
+                       hasChanged = true;
+#if NET_2_0
+                       utcLastModified = DateTime.UtcNow;
+#endif
+                       DisposeWatchers ();
+                       
+                       if (cache != null)
+                               cache.CheckExpiration ();
+
+                       return true;
+               }
+               
+               void DisposeWatchers ()
+               {
+                       lock (locker) {
+                               if (watchers != null) {
+                                       foreach (FileSystemWatcher w in watchers)
+                                               if (w != null)
+                                                       w.Dispose ();
                                }
-                       }
-
-                       if (filenames.Length > 0) {
-                               this.changedDelegate = new EventHandler (OnChanged);
-                               foreach (string s in filenames)
-                                       Watcher.AddWatch (s, changedDelegate);
+                               watchers = null;
                        }
                }
 
-               void CacheItemRemoved (string key, object value, CacheItemRemovedReason reason)
+               public void Dispose ()
                {
-                       OnChanged (this, EventArgs.Empty);
+                       DependencyDispose ();
                }
 
-               void OnChanged (object sender, EventArgs args)
+#if NET_2_0
+               internal virtual void DependencyDisposeInternal ()
                {
-                       if (changed || disposed)
-                               return;
+               }
+#endif
+               
+#if NET_2_0
+               protected virtual
+#endif
+               void DependencyDispose () 
+               {
+#if NET_2_0
+                       DependencyDisposeInternal ();
+#endif
+                       DisposeWatchers ();
+                       if (dependency != null)
+                               dependency.DependencyChanged -= new EventHandler (OnChildDependencyChanged);
+                       cache = null;
+               }
+               
+               internal void SetCache (Cache c)
+               {
+                       cache = c;
+#if NET_2_0
+                       used = c != null;
+#endif
+               }
+               
+#if NET_2_0
+               protected internal void FinishInit () 
+               {
+                       utcLastModified = DateTime.UtcNow;
+               }
 
-                       changed = true;
-                       if (Changed != null)
-                               Changed (this, new CacheDependencyChangedArgs (null));
+               internal bool IsUsed {
+                       get { return used; }
                }
 
-               public void Dispose ()
+               internal DateTime Start {
+                       get { return start; }
+                       set { start = value; }
+               }
+
+               public DateTime UtcLastModified {
+                       get {
+                               return utcLastModified;
+                       }
+               }
+
+               protected void SetUtcLastModified (DateTime utcLastModified) 
                {
+                       this.utcLastModified = utcLastModified;
                }
+#endif
+               
+               public bool HasChanged {
+                       get {
+                               if (hasChanged)
+                                       return true;
+
+                               if (DateTime.Now < start)
+                                       return false;
+
+                               if (cache != null && cachekeys != null) {
+                                       foreach (string key in cachekeys) {
+                                               if (cache.GetKeyLastChange (key) > start) {
+                                                       hasChanged = true;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (hasChanged)
+                                       DisposeWatchers ();
 
-               public bool HasChanged
+                               return hasChanged;
+                       }
+               }
+               
+               void OnChildDependencyChanged (object o, EventArgs e)
                {
-                       get { return changed; }
+                       hasChanged = true;
+                       OnDependencyChanged (o, e);
                }
+               
+               void OnDependencyChanged (object sender, EventArgs e)
+               {
+                       if (!DoOnChanged ())
+                               return;
+                       
+                       if (DependencyChanged == null)
+                               return;
 
-               internal CacheEntry [] GetCacheEntries ()
+                       foreach (EventHandler eh in DependencyChanged.GetInvocationList ())
+                               eh (sender, e);
+               }
+               
+#if NET_2_0
+               protected
+#else
+               internal
+#endif
+               void NotifyDependencyChanged (object sender, EventArgs e) 
                {
-                       return entries;
+                       OnDependencyChanged (sender, e);
                }
 
-               internal event CacheDependencyChangedHandler Changed;
+               internal event EventHandler DependencyChanged;
        }
 }
-