2007-08-17 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Caching / CacheDependency.cs
1 //
2 // System.Web.Caching.CachedDependency
3 //
4 // Author(s):
5 //  Lluis Sanchez (lluis@ximian.com)
6 //
7 // (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Collections;
31 using System.IO;
32 using System.Security.Permissions;
33 #if NET_2_0
34 using System.Text;
35 #endif
36
37 namespace System.Web.Caching
38 {
39 #if NET_2_0
40         // CAS
41         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
42         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
43         public class CacheDependency: IDisposable {
44 #else
45         // CAS - no InheritanceDemand here as the class is sealed
46         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47         public sealed class CacheDependency: IDisposable {
48 #endif
49                 string[] cachekeys;
50                 CacheDependency dependency;
51                 DateTime start;
52                 Cache cache;
53                 FileSystemWatcher[] watchers;
54                 bool hasChanged;
55 #if NET_2_0
56                 bool used;
57                 DateTime utcLastModified;
58 #endif
59                 object locker = new object ();
60                 
61 #if NET_2_0
62                 public CacheDependency (): this (null, null, null, DateTime.Now)
63                 {
64                 }
65 #endif
66                 
67                 public CacheDependency (string filename): this (new string[] { filename }, null, null, DateTime.Now)
68                 {
69                 }
70                 
71                 public CacheDependency (string[] filenames): this (filenames, null, null, DateTime.Now)
72                 {
73                 }
74                 
75                 public CacheDependency (string filename, DateTime start): this (new string[] { filename }, null, null, start)
76                 {
77                 }
78
79                 public CacheDependency (string [] filenames, DateTime start)
80                         : this (filenames, null, null, start)
81                 {
82                 }
83
84                 public CacheDependency (string[] filenames, string[] cachekeys): this (filenames, cachekeys, null, DateTime.Now)
85                 {
86                 }
87                 
88                 public CacheDependency (string[] filenames, string[] cachekeys, CacheDependency dependency): this (filenames, cachekeys, dependency, DateTime.Now)
89                 {
90                 }
91                 
92                 public CacheDependency (string[] filenames, string[] cachekeys, DateTime start): this (filenames, cachekeys, null, start)
93                 {
94                 }
95                 
96                 public CacheDependency (string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start)
97                 {
98                         if (filenames != null) {
99                                 watchers = new FileSystemWatcher [filenames.Length];
100                                 for (int n=0; n<filenames.Length; n++) {
101                                         FileSystemWatcher watcher = new FileSystemWatcher ();
102                                         if (Directory.Exists (filenames [n])) {
103                                                 watcher.Path = filenames [n];
104                                         } else {
105                                                 string parentPath = Path.GetDirectoryName (filenames [n]);
106                                                 if (parentPath != null && Directory.Exists (parentPath)) {
107                                                         watcher.Path = parentPath;
108                                                         watcher.Filter = Path.GetFileName (filenames [n]);
109                                                 } else
110                                                         continue;
111                                         }
112                                         watcher.NotifyFilter |= NotifyFilters.Size;
113                                         watcher.Created += new FileSystemEventHandler (OnChanged);
114                                         watcher.Changed += new FileSystemEventHandler (OnChanged);
115                                         watcher.Deleted += new FileSystemEventHandler (OnChanged);
116                                         watcher.Renamed += new RenamedEventHandler (OnChanged);
117                                         watcher.EnableRaisingEvents = true;
118                                         watchers [n] = watcher;
119                                 }
120                         }
121                         this.cachekeys = cachekeys;
122                         this.dependency = dependency;
123                         if (dependency != null)
124                                 dependency.DependencyChanged += new EventHandler (OnChildDependencyChanged);
125                         this.start = start;
126
127 #if NET_2_0
128                         FinishInit ();
129 #endif
130                 }
131
132 #if NET_2_0
133                 public virtual string GetUniqueID ()
134                 {
135                         StringBuilder sb = new StringBuilder ();
136                         lock (locker) {
137                                 if (watchers != null)
138                                         foreach (FileSystemWatcher fsw in watchers)
139                                                 if (fsw != null && fsw.Path != null && fsw.Path.Length != 0)
140                                                         sb.AppendFormat ("_{0}", fsw.Path);
141                         }
142
143                         if (cachekeys != null)
144                                 foreach (string key in cachekeys)
145                                         sb.AppendFormat ("_{0}", key);
146                         return sb.ToString ();
147                 }
148 #endif
149                 
150                 void OnChanged (object sender, FileSystemEventArgs args)
151                 {
152                         OnDependencyChanged (sender, args);
153                 }
154
155                 bool DoOnChanged ()
156                 {
157                         if (DateTime.Now < start)
158                                 return false;
159                         hasChanged = true;
160 #if NET_2_0
161                         utcLastModified = DateTime.UtcNow;
162 #endif
163                         DisposeWatchers ();
164                         
165                         if (cache != null)
166                                 cache.CheckExpiration ();
167
168                         return true;
169                 }
170                 
171                 void DisposeWatchers ()
172                 {
173                         lock (locker) {
174                                 if (watchers != null) {
175                                         foreach (FileSystemWatcher w in watchers)
176                                                 if (w != null)
177                                                         w.Dispose ();
178                                 }
179                                 watchers = null;
180                         }
181                 }
182
183                 public void Dispose ()
184                 {
185                         DependencyDispose ();
186                 }
187
188 #if NET_2_0
189                 internal virtual void DependencyDisposeInternal ()
190                 {
191                 }
192 #endif
193                 
194 #if NET_2_0
195                 protected virtual
196 #endif
197                 void DependencyDispose () 
198                 {
199 #if NET_2_0
200                         DependencyDisposeInternal ();
201 #endif
202                         DisposeWatchers ();
203                         if (dependency != null)
204                                 dependency.DependencyChanged -= new EventHandler (OnChildDependencyChanged);
205                         cache = null;
206                 }
207                 
208                 internal void SetCache (Cache c)
209                 {
210                         cache = c;
211 #if NET_2_0
212                         used = c != null;
213 #endif
214                 }
215                 
216 #if NET_2_0
217                 protected internal void FinishInit () 
218                 {
219                         utcLastModified = DateTime.UtcNow;
220                 }
221
222                 internal bool IsUsed {
223                         get { return used; }
224                 }
225
226                 internal DateTime Start {
227                         get { return start; }
228                         set { start = value; }
229                 }
230
231                 public DateTime UtcLastModified {
232                         get {
233                                 return utcLastModified;
234                         }
235                 }
236
237                 protected void SetUtcLastModified (DateTime utcLastModified) 
238                 {
239                         this.utcLastModified = utcLastModified;
240                 }
241 #endif
242                 
243                 public bool HasChanged {
244                         get {
245                                 if (hasChanged)
246                                         return true;
247
248                                 if (DateTime.Now < start)
249                                         return false;
250
251                                 if (cache != null && cachekeys != null) {
252                                         foreach (string key in cachekeys) {
253                                                 if (cache.GetKeyLastChange (key) > start) {
254                                                         hasChanged = true;
255                                                         break;
256                                                 }
257                                         }
258                                 }
259                                 if (hasChanged)
260                                         DisposeWatchers ();
261
262                                 return hasChanged;
263                         }
264                 }
265                 
266                 void OnChildDependencyChanged (object o, EventArgs e)
267                 {
268                         hasChanged = true;
269                         OnDependencyChanged (o, e);
270                 }
271                 
272                 void OnDependencyChanged (object sender, EventArgs e)
273                 {
274                         if (!DoOnChanged ())
275                                 return;
276                         
277                         if (DependencyChanged == null)
278                                 return;
279
280                         foreach (EventHandler eh in DependencyChanged.GetInvocationList ())
281                                 eh (sender, e);
282                 }
283                 
284 #if NET_2_0
285                 protected
286 #else
287                 internal
288 #endif
289                 void NotifyDependencyChanged (object sender, EventArgs e) 
290                 {
291                         OnDependencyChanged (sender, e);
292                 }
293
294                 internal event EventHandler DependencyChanged;
295         }
296 }