New tests.
[mono.git] / mcs / class / System.Runtime.Caching / System.Runtime.Caching / HostFileChangeMonitor.cs
1 //
2 // HostFileChangeMonitor.cs
3 //
4 // Authors:
5 //      Marek Habersack <mhabersack@novell.com>
6 //
7 // Copyright (C) 2010 Novell, Inc. (http://novell.com/)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.IO;
32 using System.Runtime.Caching.Hosting;
33 using System.Text;
34
35 namespace System.Runtime.Caching
36 {
37         public sealed class HostFileChangeMonitor : FileChangeMonitor
38         {
39                 const long INVALID_FILE_SIZE = -1L;
40                 static readonly object notificationSystemLock = new object ();
41                 static readonly DateTime missingFileTimeStamp = new DateTime (0x701CE1722770000);
42                 static IFileChangeNotificationSystem notificationSystem;
43                 static bool notificationSystemSetFromHost;
44                 
45                 ReadOnlyCollection <string> filePaths;
46                 DateTime lastModified;
47                 string uniqueId;
48                 bool disposeNotificationSystem;
49
50                 Dictionary <string, object> notificationStates;
51                 
52                 public override ReadOnlyCollection<string> FilePaths {
53                         get { return filePaths;}
54                 }
55                 
56                 public override DateTimeOffset LastModified {
57                         get { return lastModified; }
58                 }
59                 
60                 public override string UniqueId {
61                         get { return uniqueId; }
62                 }
63                 
64                 public HostFileChangeMonitor (IList<string> filePaths)
65                 {
66                         if (filePaths == null)
67                                 throw new ArgumentNullException ("filePaths");
68
69                         if (filePaths.Count == 0)
70                                 throw new ArgumentException ("filePaths contains zero items.");
71
72                         var list = new List <string> (filePaths.Count);
73                         var sb = new StringBuilder ();
74                         lastModified = DateTime.MinValue;
75                         DateTime lastWrite;
76                         long size;
77                         bool ignoreForUniqueId;
78                         
79                         foreach (string p in filePaths) {
80                                 if (String.IsNullOrEmpty (p))
81                                         throw new ArgumentException ("A path in the filePaths list is null or an empty string.");
82
83                                 if (!Path.IsPathRooted (p))
84                                         throw new ArgumentException ("Absolute path information is required.");
85
86                                 if (list.Contains (p))
87                                         ignoreForUniqueId = true;
88                                 else
89                                         ignoreForUniqueId = false;
90                                 
91                                 list.Add (p);
92
93                                 if (ignoreForUniqueId)
94                                         continue;
95                                 
96                                 if (Directory.Exists (p)) {
97                                         var di = new DirectoryInfo (p);
98                                         lastWrite = di.LastWriteTimeUtc;
99                                         size = INVALID_FILE_SIZE;
100                                 } else if (File.Exists (p)) {
101                                         var fi = new FileInfo (p);
102                                         lastWrite = fi.LastWriteTimeUtc;
103                                         size = fi.Length;
104                                 } else {
105                                         lastWrite = missingFileTimeStamp;
106                                         size = INVALID_FILE_SIZE;
107                                 }
108                                 
109                                 if (lastWrite > lastModified)
110                                         lastModified = lastWrite;
111
112                                 sb.AppendFormat ("{0}{1:X}{2:X}", p, lastWrite.Ticks, (ulong)size);
113                         }
114
115                         this.filePaths = new ReadOnlyCollection <string> (list);
116                         this.uniqueId = sb.ToString ();
117
118                         bool initComplete = false;
119                         try {
120                                 InitMonitoring ();
121                                 initComplete = true;
122                         } finally {
123                                 InitializationComplete ();
124                                 if (!initComplete)
125                                         Dispose ();
126                         }
127                 }
128
129                 void InitMonitoring ()
130                 {
131                         IServiceProvider host;
132                         if (!notificationSystemSetFromHost && (host = ObjectCache.Host) != null) {
133                                 lock (notificationSystemLock) {
134                                         if (!notificationSystemSetFromHost) {
135                                                 if (host != null)
136                                                         notificationSystem = host.GetService (typeof (IFileChangeNotificationSystem)) as IFileChangeNotificationSystem;
137
138                                                 if (notificationSystem != null)
139                                                         notificationSystemSetFromHost = true;
140                                         }
141                                 }
142                         }
143
144                         if (!notificationSystemSetFromHost) {
145                                 notificationSystem = new FileChangeNotificationSystem ();
146                                 disposeNotificationSystem = true;
147                         }
148
149                         object state;
150
151                         // TODO: what are these two used for?
152                         DateTimeOffset lastWriteTime;
153                         long fileSize;
154                         
155                         notificationStates = new Dictionary <string, object> (filePaths.Count, Helpers.StringEqualityComparer);
156                         foreach (string path in filePaths) {
157                                 if (notificationStates.ContainsKey (path))
158                                         continue; // silently ignore dupes
159                                 
160                                 notificationSystem.StartMonitoring (path, OnChanged, out state, out lastWriteTime, out fileSize);
161                                 notificationStates.Add (path, state);
162                         }
163                 }
164
165                 protected override void Dispose (bool disposing)
166                 {
167                         if (disposing && notificationSystem != null) {
168                                 object state;
169                                 
170                                 foreach (var de in notificationStates) {
171                                         state = de.Value;
172                                         if (state == null)
173                                                 continue;
174                                         
175                                         try {
176                                                 notificationSystem.StopMonitoring (de.Key, state);
177                                         } catch {
178                                                 // ignore
179                                         }
180                                 }
181                                 
182                                 if (disposeNotificationSystem) {
183                                         var fcns = notificationSystem as FileChangeNotificationSystem;
184                                         if (fcns != null) {
185                                                 try {
186                                                         fcns.Dispose ();
187                                                 } finally {
188                                                         notificationSystem = null;
189                                                 }
190                                         }
191                                 }
192                         }
193                 }
194         }
195 }