Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / StatusKeeper.cs @ d15e99b4

History | View | Annotate | Download (13.1 kB)

1
using System;
2
using System.Collections;
3
using System.Collections.Concurrent;
4
using System.Collections.Generic;
5
using System.ComponentModel.Composition;
6
using System.Diagnostics;
7
using System.Diagnostics.Contracts;
8
using System.IO;
9
using System.Linq;
10
using System.Linq.Expressions;
11
using System.Security.Cryptography;
12
using System.Text;
13
using System.Threading;
14
using System.Threading.Tasks;
15
using Castle.ActiveRecord;
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 JobQueue _statusUpdateQueue;
28

    
29
        public StatusKeeper()
30
        {            
31
            var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
32
            var pithosDbPath = Path.Combine(appDataPath , "Pithos");
33

    
34
            var source = GetConfiguration(pithosDbPath);
35
            ActiveRecordStarter.Initialize(source,typeof(FileState),typeof(FileTag));            
36
            
37
            if (!File.Exists(Path.Combine(pithosDbPath ,"pithos.db")))
38
                ActiveRecordStarter.CreateSchema();
39

    
40
        }
41

    
42
        private static InPlaceConfigurationSource GetConfiguration(string pithosDbPath)
43
        {
44
            var properties = new Dictionary<string, string>
45
                                 {
46
                                     {"connection.driver_class", "NHibernate.Driver.SQLite20Driver"},
47
                                     {"dialect", "NHibernate.Dialect.SQLiteDialect"},
48
                                     {"connection.provider", "NHibernate.Connection.DriverConnectionProvider"},
49
                                     {
50
                                         "proxyfactory.factory_class",
51
                                         "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle"
52
                                         },
53
                                 };
54

    
55
            var connectionString = String.Format(@"Data Source={0}\pithos.db;Version=3", pithosDbPath);
56
            properties.Add("connection.connection_string", connectionString);
57

    
58
            var source = new InPlaceConfigurationSource();
59

    
60
            source.Add(typeof (ActiveRecordBase), properties);
61
            return source;
62
        }
63

    
64
        public void StartProcessing(CancellationToken token)
65
        {
66
            _statusUpdateQueue.Start(token);
67
            
68
        }
69

    
70
       
71

    
72
        public void Stop()
73
        {
74
            _statusUpdateQueue.Stop();            
75
        }
76
       
77

    
78
        public IEnumerable<string> StoreUnversionedFiles(ParallelQuery<string> paths)
79
        {            
80
            var existingFiles = from state in  FileState.Queryable
81
                                    select state.FilePath;
82

    
83
            var newFiles = (from file in paths.Except(existingFiles.AsParallel())
84
                            select new FileState
85
                                       {
86
                                           FilePath = file,
87
                                           OverlayStatus = FileOverlayStatus.Unversioned,
88
                                           FileStatus=FileStatus.Created,     
89
                                           Checksum=Signature.CalculateHash(file)
90
                                       }
91
                           );
92
            
93
            //var files=new ConcurrentBag<string>();
94
            newFiles.ForAll(state=> _statusUpdateQueue.Add(state.Save));
95

    
96
            return newFiles.Select(state => state.FilePath);// files.GetConsumingEnumerable();
97

    
98
        }
99

    
100
/*
101
        private static Task<string> CalculateHashAsync(string path)
102
        {
103

    
104
            string hash;
105
            using (var hasher = MD5.Create())
106
            {
107
                return FileAsync.ReadAllBytes(path)
108
                    .ContinueWith(t => hasher.ComputeHash(t.Result))
109
                    .ContinueWith(t =>
110
                                      {
111
                                          //var hashBuilder = new StringBuilder();
112
                                          return (from byte b in t.Result.AsParallel()
113
                                                  select b.ToString("x2").ToLower()).Aggregate((s1, s2) => s1 + s2);                                         
114
                                      });
115
            }
116
            /*using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true))
117
            {
118
                
119
                stream.ReadAllBytes()
120
                    .ContinueWith(result => hasher.ComputeHash(result.Result))
121
                    .;
122
                var hashBytes = hasher.ComputeHash(stream);
123
                var hashBuilder = new StringBuilder();
124
                foreach (byte b in hasher.ComputeHash(stream))
125
                    hashBuilder.Append(b.ToString("x2").ToLower());
126
                hash = hashBuilder.ToString();
127

    
128
            }
129
            return hash;#1#
130
        }
131
*/
132

    
133

    
134
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
135

    
136
        public void SetPithosStatus(PithosStatus status)
137
        {
138
            _pithosStatus = status;
139
        }
140

    
141
        public PithosStatus GetPithosStatus()
142
        {
143
            return _pithosStatus;
144
        }
145

    
146

    
147
        ConcurrentDictionary<string,NetworkState> _networkState=new ConcurrentDictionary<string, NetworkState>();
148

    
149
        public void SetNetworkState(string path,NetworkState state)
150
        {
151
            _networkState[path.ToLower()] = state;
152
            //Removing may fail so we store the "None" value anyway
153
            if (state == NetworkState.None)
154
            {
155
                NetworkState oldState;
156
                _networkState.TryRemove(path, out oldState);
157
            }
158
        }
159

    
160
        public NetworkState GetNetworkState(string path)
161
        {
162
            NetworkState state ;
163
            if (_networkState.TryGetValue(path, out state))
164
                return state;
165
            return NetworkState.None;
166
        }
167

    
168
        public T GetStatus<T>(string path,Func<FileState,T> getter,T defaultValue )
169
        {
170

    
171

    
172
            try
173
            {
174
                var state = FileState.TryFind(path.ToLower());
175
                return state == null ? defaultValue : getter(state);
176
            }
177
            catch (Exception exc)
178
            {
179
                Trace.TraceError(exc.ToString());
180
                return defaultValue;
181
            }
182
        }
183

    
184
        /// <summary>
185
        /// Sets the status of a file, creating a new FileState entry if one doesn't already exist.
186
        /// </summary>
187
        /// <param name="path"></param>
188
        /// <param name="setter"></param>
189
        public void SetStatus(string path,Action<FileState> setter)
190
        {
191
            if (String.IsNullOrWhiteSpace(path))
192
                throw new ArgumentNullException("path", "path can't be empty");
193

    
194
            if (setter==null)
195
                throw new ArgumentNullException("setter", "setter can't be empty");
196

    
197
            _statusUpdateQueue.Add(() =>
198
            {
199
                var filePath = path.ToLower();
200
                var state = FileState.TryFind(filePath);
201
                if (state != null)
202
                {
203
                    setter(state);
204
                    state.Update();
205
                }
206
                else
207
                {
208
                    state = new FileState {FilePath = filePath};
209
                    setter(state);
210
                    state.Save();
211
                }
212
            });
213
        }
214

    
215
        /// <summary>
216
        /// Sets the status of a file only if the file already exists
217
        /// </summary>
218
        /// <param name="path"></param>
219
        /// <param name="setter"></param>
220
        private void UpdateStatus(string path, Action<FileState> setter)
221
        {
222
            if (String.IsNullOrWhiteSpace(path))
223
                throw new ArgumentNullException("path", "path can't be empty");
224

    
225
            if (setter == null)
226
                throw new ArgumentNullException("setter", "setter can't be empty");
227

    
228
            _statusUpdateQueue.Add(() =>
229
            {
230
                var filePath = path.ToLower();
231
                var state = FileState.TryFind(filePath);
232
                if (state == null)
233
                {
234
                    Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
235
                    return;
236
                }
237
                setter(state);
238
                state.Save();
239
            });
240
        }
241

    
242
        public FileOverlayStatus GetFileOverlayStatus(string path)
243
        {
244
            try
245
            {
246
                var state = FileState.TryFind(path.ToLower());
247
                return state == null ? FileOverlayStatus.Unversioned : state.OverlayStatus;
248
            }
249
            catch (Exception exc)
250
            {
251
                Trace.TraceError(exc.ToString());
252
                return FileOverlayStatus.Unversioned;
253
            }
254
        }
255

    
256
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
257
        {
258
            SetStatus(path,s=>s.OverlayStatus=overlayStatus);
259
        }
260

    
261
        public void RemoveFileOverlayStatus(string path)
262
        {
263
            _statusUpdateQueue.Add(() =>
264
                InnerRemoveFileOverlayStatus(path));
265
        }
266

    
267
        private static void InnerRemoveFileOverlayStatus(string path)
268
        {
269
            FileState.DeleteAll(new[] {path});
270
        }
271

    
272
        public void RenameFileOverlayStatus(string oldPath, string newPath)
273
        {
274
            _statusUpdateQueue.Add(() =>
275
                InnerRenameFileOverlayStatus(oldPath, newPath));
276
        }
277

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

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

    
303
        public void StoreInfo(string path,ObjectInfo objectInfo)
304
        {
305
            if (String.IsNullOrWhiteSpace(path))
306
                throw new ArgumentNullException("path", "path can't be empty");
307
            if (objectInfo==null)
308
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
309

    
310
            var state = FileState.TryFind(path)??new FileState();
311
            state.FilePath = path.ToLower();
312
            state.Checksum = objectInfo.Hash;
313
            state.FileStatus = FileStatus.Unchanged;
314
            state.OverlayStatus = FileOverlayStatus.Normal;
315
            foreach (var tag in objectInfo.Tags)
316
            {
317
                state.Tags.Add(new FileTag
318
                                   {
319
                                       FilePath=state.FilePath,
320
                                       FileState=state,
321
                                       Value=tag.Value
322
                                   });
323
            }
324
            
325
            state.Save();
326
            
327
        }
328

    
329
        
330
        public void SetFileStatus(string path, FileStatus status)
331
        {            
332
            UpdateStatus(path, state=>state.FileStatus = status);
333
        }
334

    
335

    
336
       /* private static void InnerSetFileStatus(string path, FileStatus status)
337
        {
338
            var filePath = path.ToLower();
339
            var state = FileState.TryFind(filePath);
340
            if (state == null)
341
            {
342
                Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
343
                return;
344
            }
345
            state.FileStatus = status;
346
            state.Update();
347
        }*/
348

    
349
        public FileStatus GetFileStatus(string path)
350
        {
351
            var state = FileState.TryFind(path.ToLower());
352
            return (state==null)?FileStatus.Missing:state.FileStatus ;
353
        }
354

    
355
        public void ClearFileStatus(string path)
356
        {
357
            //TODO:SHOULDN'T need both clear file status and remove overlay status
358
            _statusUpdateQueue.Add(()=>
359
                FileState.DeleteAll(new[] { path.ToLower() }));   
360
        }
361

    
362
        public void UpdateFileChecksum(string path, string checksum)
363
        {
364
            var state = FileState.TryFind(path);
365
            if (state == null)
366
            {
367
                Trace.TraceWarning("[NOFILE] Unable to set checkesum for {0}.",path);
368
                return;
369
            }
370
            state.Checksum = checksum;
371
            state.Update();
372
        }
373
    }
374

    
375
    public enum NetworkState
376
    {
377
        None,
378
        Uploading,
379
        Downloading
380
    }
381
}