Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (13.2 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=new JobQueue();
28

    
29
        public StatusKeeper()
30
        {            
31
            var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
32
            var pithosDbPath = Path.Combine(appDataPath , "Pithos");
33
            if (!Directory.Exists(pithosDbPath))
34
                Directory.CreateDirectory(pithosDbPath);
35

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

    
42
        }
43

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

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

    
60
            var source = new InPlaceConfigurationSource();
61

    
62
            source.Add(typeof (ActiveRecordBase), properties);
63
            return source;
64
        }
65

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

    
72
       
73

    
74
        public void Stop()
75
        {
76
            _statusUpdateQueue.Stop();            
77
        }
78
       
79

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

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

    
98
            return newFiles.Select(state => state.FilePath);// files.GetConsumingEnumerable();
99

    
100
        }
101

    
102
/*
103
        private static Task<string> CalculateHashAsync(string path)
104
        {
105

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

    
130
            }
131
            return hash;#1#
132
        }
133
*/
134

    
135

    
136
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
137

    
138
        public void SetPithosStatus(PithosStatus status)
139
        {
140
            _pithosStatus = status;
141
        }
142

    
143
        public PithosStatus GetPithosStatus()
144
        {
145
            return _pithosStatus;
146
        }
147

    
148

    
149
        ConcurrentDictionary<string,NetworkState> _networkState=new ConcurrentDictionary<string, NetworkState>();
150

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

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

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

    
173

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
337

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

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

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

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

    
377
    public enum NetworkState
378
    {
379
        None,
380
        Uploading,
381
        Downloading
382
    }
383
}