Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / StatusKeeper.cs @ 283809f3

History | View | Annotate | Download (12.2 kB)

1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.ComponentModel.Composition;
5
using System.Diagnostics;
6
using System.Diagnostics.Contracts;
7
using System.IO;
8
using System.Linq;
9
using System.Linq.Expressions;
10
using System.Security.Cryptography;
11
using System.Text;
12
using System.Threading;
13
using System.Threading.Tasks;
14
using Castle.ActiveRecord;
15
using Castle.ActiveRecord.Framework;
16
using Castle.ActiveRecord.Framework.Config;
17
using Pithos.Interfaces;
18

    
19
namespace Pithos.Core
20
{
21
    [Export(typeof(IStatusChecker)),Export(typeof(IStatusKeeper))]
22
    public class StatusKeeper:IStatusChecker,IStatusKeeper
23
    {
24
        [System.ComponentModel.Composition.Import]
25
        public IPithosSettings Settings { get; set; }
26

    
27
        private BlockingCollection<Action> _statusUpdateQueue = new BlockingCollection<Action>();
28
        //private readonly CancellationToken _cancel=new CancellationToken();
29

    
30
        public StatusKeeper()
31
        {
32
            var source = new XmlConfigurationSource("DbConfig.xml");
33
            ActiveRecordStarter.Initialize(source,typeof(FileState));            
34
            
35
            if (!File.Exists("pithos.db"))
36
                ActiveRecordStarter.CreateSchema();
37

    
38
            Task.Factory.StartNew(ProcessUpdates);
39
        }
40

    
41
        public void ProcessUpdates()
42
        {            
43
            foreach (var action in _statusUpdateQueue.GetConsumingEnumerable())
44
            {
45
                action();
46
            }
47
        }
48

    
49
        public void Stop()
50
        {
51
            _statusUpdateQueue.CompleteAdding();
52
        }
53
       
54

    
55
        public IEnumerable<string> StoreUnversionedFiles(ParallelQuery<string> paths)
56
        {            
57
            var existingFiles = from state in  FileState.Queryable
58
                                    select state.FilePath;
59

    
60
            var newFiles = (from file in paths.Except(existingFiles.AsParallel())
61
                            select new FileState
62
                                       {
63
                                           FilePath = file,
64
                                           OverlayStatus = FileOverlayStatus.Unversioned,
65
                                           FileStatus=FileStatus.Created,     
66
                                           Checksum=Signature.CalculateHash(file)
67
                                       }
68
                           );
69
            
70
            //var files=new ConcurrentBag<string>();
71
            newFiles.ForAll(state=> _statusUpdateQueue.Add(state.Save));
72

    
73
            return newFiles.Select(state => state.FilePath);// files.GetConsumingEnumerable();
74

    
75
        }
76

    
77
/*
78
        private static Task<string> CalculateHashAsync(string path)
79
        {
80

    
81
            string hash;
82
            using (var hasher = MD5.Create())
83
            {
84
                return FileAsync.ReadAllBytes(path)
85
                    .ContinueWith(t => hasher.ComputeHash(t.Result))
86
                    .ContinueWith(t =>
87
                                      {
88
                                          //var hashBuilder = new StringBuilder();
89
                                          return (from byte b in t.Result.AsParallel()
90
                                                  select b.ToString("x2").ToLower()).Aggregate((s1, s2) => s1 + s2);                                         
91
                                      });
92
            }
93
            /*using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true))
94
            {
95
                
96
                stream.ReadAllBytes()
97
                    .ContinueWith(result => hasher.ComputeHash(result.Result))
98
                    .;
99
                var hashBytes = hasher.ComputeHash(stream);
100
                var hashBuilder = new StringBuilder();
101
                foreach (byte b in hasher.ComputeHash(stream))
102
                    hashBuilder.Append(b.ToString("x2").ToLower());
103
                hash = hashBuilder.ToString();
104

    
105
            }
106
            return hash;#1#
107
        }
108
*/
109

    
110

    
111
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
112

    
113
        public void SetPithosStatus(PithosStatus status)
114
        {
115
            _pithosStatus = status;
116
        }
117

    
118
        public PithosStatus GetPithosStatus()
119
        {
120
            return _pithosStatus;
121
        }
122

    
123

    
124
        ConcurrentDictionary<string,NetworkState> _networkState=new ConcurrentDictionary<string, NetworkState>();
125

    
126
        public void SetNetworkState(string path,NetworkState state)
127
        {
128
            _networkState[path.ToLower()] = state;
129
            //Removing may fail so we store the "None" value anyway
130
            if (state == NetworkState.None)
131
            {
132
                NetworkState oldState;
133
                _networkState.TryRemove(path, out oldState);
134
            }
135
        }
136

    
137
        public NetworkState GetNetworkState(string path)
138
        {
139
            NetworkState state ;
140
            if (_networkState.TryGetValue(path, out state))
141
                return state;
142
            return NetworkState.None;
143
        }
144

    
145

    
146
        public T GetStatus<T>(string path,Func<FileState,T> getter,T defaultValue )
147
        {
148

    
149

    
150
            try
151
            {
152
                var state = FileState.TryFind(path.ToLower());
153
                return state == null ? defaultValue : getter(state);
154
            }
155
            catch (Exception exc)
156
            {
157
                Trace.TraceError(exc.ToString());
158
                return defaultValue;
159
            }
160
        }
161

    
162
        /// <summary>
163
        /// Sets the status of a file, creating a new FileState entry if one doesn't already exist.
164
        /// </summary>
165
        /// <param name="path"></param>
166
        /// <param name="setter"></param>
167
        public void SetStatus(string path,Action<FileState> setter)
168
        {
169
            if (String.IsNullOrWhiteSpace(path))
170
                throw new ArgumentNullException("path", "path can't be empty");
171

    
172
            if (setter==null)
173
                throw new ArgumentNullException("setter", "setter can't be empty");
174

    
175
            _statusUpdateQueue.Add(() =>
176
            {
177
                var filePath = path.ToLower();
178
                var state = FileState.TryFind(filePath);
179
                if (state != null)
180
                {
181
                    setter(state);
182
                    state.Update();
183
                }
184
                else
185
                {
186
                    state = new FileState {FilePath = filePath};
187
                    setter(state);
188
                    state.Save();
189
                }
190
            });
191
        }
192

    
193
        /// <summary>
194
        /// Sets the status of a file only if the file already exists
195
        /// </summary>
196
        /// <param name="path"></param>
197
        /// <param name="setter"></param>
198
        private void UpdateStatus(string path, Action<FileState> setter)
199
        {
200
            if (String.IsNullOrWhiteSpace(path))
201
                throw new ArgumentNullException("path", "path can't be empty");
202

    
203
            if (setter == null)
204
                throw new ArgumentNullException("setter", "setter can't be empty");
205

    
206
            _statusUpdateQueue.Add(() =>
207
            {
208
                var filePath = path.ToLower();
209
                var state = FileState.TryFind(filePath);
210
                if (state == null)
211
                {
212
                    Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
213
                    return;
214
                }
215
                setter(state);
216
                state.Save();
217
            });
218
        }
219

    
220
        public FileOverlayStatus GetFileOverlayStatus(string path)
221
        {
222
            try
223
            {
224
                var state = FileState.TryFind(path.ToLower());
225
                return state == null ? FileOverlayStatus.Unversioned : state.OverlayStatus;
226
            }
227
            catch (Exception exc)
228
            {
229
                Trace.TraceError(exc.ToString());
230
                return FileOverlayStatus.Unversioned;
231
            }
232
        }
233

    
234
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
235
        {
236
            SetStatus(path,s=>s.OverlayStatus=overlayStatus);
237
        }
238

    
239
       /* private static void InnerSetOverlayStatus(string path, FileOverlayStatus overlayStatus)
240
        {
241
            var filePath = path.ToLower();
242
            var state = FileState.TryFind(filePath);
243
            if (state != null)
244
            {
245
                state.OverlayStatus = overlayStatus;
246
                state.Update();
247
            }
248
            else
249
            {
250
                state = new FileState
251
                            {FilePath = filePath, OverlayStatus = overlayStatus};
252
                state.Save();
253
            }
254
        }*/
255

    
256
        public void RemoveFileOverlayStatus(string path)
257
        {
258
            _statusUpdateQueue.Add(() =>
259
                InnerRemoveFileOverlayStatus(path));
260
        }
261

    
262
        private static void InnerRemoveFileOverlayStatus(string path)
263
        {
264
            FileState.DeleteAll(new[] {path});
265
        }
266

    
267
        public void RenameFileOverlayStatus(string oldPath, string newPath)
268
        {
269
            _statusUpdateQueue.Add(() =>
270
                InnerRenameFileOverlayStatus(oldPath, newPath));
271
        }
272

    
273
        private static void InnerRenameFileOverlayStatus(string oldPath, string newPath)
274
        {
275
            var state = FileState.TryFind(oldPath);
276
            if (state == null)
277
            {
278
                Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", oldPath);
279
                return;
280
            }
281
            //NOTE: This will cause problems if path is used as a key in relationships
282
            state.FilePath = newPath;
283
            state.Update();
284
        }
285

    
286
        public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
287
        {
288
            if (String.IsNullOrWhiteSpace(path))
289
                throw new ArgumentNullException("path", "path can't be empty");
290
            
291
            UpdateStatus(path,state=>
292
                                  {
293
                                      state.FileStatus = fileStatus;
294
                                      state.OverlayStatus = overlayStatus;
295
                                  });            
296
        }
297

    
298
        public void StoreInfo(string path,ObjectInfo objectInfo)
299
        {
300
            if (String.IsNullOrWhiteSpace(path))
301
                throw new ArgumentNullException("path", "path can't be empty");
302
            if (objectInfo==null)
303
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
304

    
305
            var state = FileState.TryFind(path)??new FileState();
306
            state.FilePath = path.ToLower();
307
            state.Checksum = objectInfo.Hash;
308
            state.FileStatus = FileStatus.Unchanged;
309
            state.OverlayStatus = FileOverlayStatus.Normal;            
310
            
311
            state.Save();
312
            
313
        }
314

    
315
        
316
        public void SetFileStatus(string path, FileStatus status)
317
        {            
318
            UpdateStatus(path, state=>state.FileStatus = status);
319
        }
320

    
321

    
322
       /* private static void InnerSetFileStatus(string path, FileStatus status)
323
        {
324
            var filePath = path.ToLower();
325
            var state = FileState.TryFind(filePath);
326
            if (state == null)
327
            {
328
                Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
329
                return;
330
            }
331
            state.FileStatus = status;
332
            state.Update();
333
        }*/
334

    
335
        public FileStatus GetFileStatus(string path)
336
        {
337
            var state = FileState.TryFind(path.ToLower());
338
            return (state==null)?FileStatus.Missing:state.FileStatus ;
339
        }
340

    
341
        public void ClearFileStatus(string path)
342
        {
343
            //TODO:SHOULDN'T need both clear file status and remove overlay status
344
            _statusUpdateQueue.Add(()=>
345
                FileState.DeleteAll(new[] { path.ToLower() }));   
346
        }
347

    
348
        public void UpdateFileChecksum(string path, string checksum)
349
        {
350
            var state = FileState.TryFind(path);
351
            if (state == null)
352
            {
353
                Trace.TraceWarning("[NOFILE] Unable to set checkesum for {0}.",path);
354
                return;
355
            }
356
            state.Checksum = checksum;
357
            state.Update();
358
        }
359
    }
360

    
361
    public enum NetworkState
362
    {
363
        None,
364
        Uploading,
365
        Downloading
366
    }
367
}