Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (27.9 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.Net.NetworkInformation;
10
using System.Security.Cryptography;
11
using System.ServiceModel.Description;
12
using System.Text;
13
using System.Threading;
14
using System.Threading.Tasks;
15
using Castle.ActiveRecord.Queries;
16
using Microsoft.WindowsAPICodePack.Net;
17
using Pithos.Interfaces;
18
using System.ServiceModel;
19

    
20
namespace Pithos.Core
21
{
22
    [Export(typeof(PithosMonitor))]
23
    public class PithosMonitor:IDisposable
24
    {
25
        private const string PithosContainer = "pithos";
26
        private const string TrashContainer = "trash";
27

    
28
        [Import]
29
        public IPithosSettings Settings{get;set;}
30

    
31
        [Import]
32
        public IStatusKeeper StatusKeeper { get; set; }
33

    
34
        [Import]
35
        public IPithosWorkflow Workflow { get; set; }
36

    
37
        [Import]
38
        public ICloudClient CloudClient { get; set; }
39

    
40
        [Import]
41
        public ICloudClient CloudListeningClient { get; set; }
42

    
43
        public string UserName { get; set; }
44
        public string ApiKey { get; set; }
45

    
46
        private ServiceHost _statusService { get; set; }
47

    
48
        private FileSystemWatcher _watcher;
49

    
50
        public bool Pause
51
        {
52
            get { return _watcher == null || !_watcher.EnableRaisingEvents; }
53
            set
54
            {
55
                if (_watcher!=null)
56
                    _watcher.EnableRaisingEvents = !value;
57
                if (value)
58
                {
59
                    StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused);
60
                }
61
                else
62
                {
63
                    StatusKeeper.SetPithosStatus(PithosStatus.InSynch);
64
                }
65
            }
66
        }
67

    
68
        public string RootPath { get; set; }
69

    
70

    
71
        CancellationTokenSource _cancellationSource;
72

    
73
        readonly BlockingCollection<WorkflowState> _fileEvents = new BlockingCollection<WorkflowState>();
74
        readonly BlockingCollection<WorkflowState> _uploadEvents = new BlockingCollection<WorkflowState>();
75

    
76
        
77

    
78
        public void Start()
79
        {
80

    
81
            if (_cancellationSource != null)
82
            {
83
                if (!_cancellationSource.IsCancellationRequested)
84
                    return;
85
            }
86
            _cancellationSource = new CancellationTokenSource();
87

    
88
            var proxyUri = ProxyFromSettings();            
89
            CloudClient.Proxy = proxyUri;
90
            CloudClient.UsePithos = this.UsePithos;
91
            EnsurePithosContainers();
92
            StatusKeeper.StartProcessing(_cancellationSource.Token);
93
            IndexLocalFiles(RootPath);
94
            StartMonitoringFiles(RootPath);
95

    
96
            StartStatusService();
97

    
98
            StartNetwork();
99
        }
100

    
101
        private void EnsurePithosContainers()
102
        {
103
            CloudClient.UsePithos = this.UsePithos;
104
            CloudClient.AuthenticationUrl = this.AuthenticationUrl;
105
            CloudClient.Authenticate(UserName, ApiKey);
106

    
107
            var pithosContainers = new[] {PithosContainer, TrashContainer};
108
            foreach (var container in pithosContainers)
109
            {
110
                if (!CloudClient.ContainerExists(container))
111
                    CloudClient.CreateContainer(container);                
112
            }
113
        }
114

    
115
        public string AuthenticationUrl { get; set; }
116

    
117
        private Uri ProxyFromSettings()
118
        {            
119
            if (Settings.UseManualProxy)
120
            {
121
                var proxyUri = new UriBuilder
122
                                   {
123
                                       Host = Settings.ProxyServer, 
124
                                       Port = Settings.ProxyPort
125
                                   };
126
                if (Settings.ProxyAuthentication)
127
                {
128
                    proxyUri.UserName = Settings.ProxyUsername;
129
                    proxyUri.Password = Settings.ProxyPassword;
130
                }
131
                return proxyUri.Uri;
132
            }
133
            return null;
134
        }
135

    
136
        private void IndexLocalFiles(string path)
137
        {
138
            Trace.TraceInformation("[START] Inxed Local");
139
            try
140
            {
141
                var files =
142
                    from filePath in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).AsParallel()
143
                    select filePath;
144
                StatusKeeper.StoreUnversionedFiles(files);
145

    
146
                RestartInterruptedFiles();
147
            }
148
            catch (Exception exc)
149
            {
150
                Trace.TraceError("[ERROR] Index Local - {0}", exc);
151
            }
152
            finally
153
            {
154
                Trace.TraceInformation("[END] Inxed Local");
155
            }
156
        }
157

    
158
        private void RestartInterruptedFiles()
159
        {
160
            var interruptedStates = new[] { FileOverlayStatus.Unversioned, FileOverlayStatus.Modified };
161
            var filesQuery = from state in FileState.Queryable
162
                             where interruptedStates.Contains(state.OverlayStatus)
163
                             select new WorkflowState
164
                                      {
165
                                          Path = state.FilePath.ToLower(),
166
                                          FileName = Path.GetFileName(state.FilePath).ToLower(),
167
                                          Status=state.OverlayStatus==FileOverlayStatus.Unversioned?
168
                                                            FileStatus.Created:
169
                                                            FileStatus.Modified,
170
                                          TriggeringChange = state.OverlayStatus==FileOverlayStatus.Unversioned?
171
                                                            WatcherChangeTypes.Created:
172
                                                            WatcherChangeTypes.Changed
173
                                      };
174
            _uploadEvents.AddFromEnumerable(filesQuery,false);           
175
            
176
        }
177

    
178
        private void StartStatusService()
179
        {
180
            // Create a ServiceHost for the CalculatorService type and provide the base address.
181
            var baseAddress = new Uri("net.pipe://localhost/pithos");
182
            _statusService = new ServiceHost(typeof(StatusService), baseAddress);
183
            
184
            var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
185
            
186
            _statusService.AddServiceEndpoint(typeof(IStatusService), binding, "net.pipe://localhost/pithos/statuscache");
187
            _statusService.AddServiceEndpoint(typeof (ISettingsService), binding, "net.pipe://localhost/pithos/settings");
188

    
189

    
190
            //// Add a mex endpoint
191
            var smb = new ServiceMetadataBehavior
192
                          {
193
                              HttpGetEnabled = true, 
194
                              HttpGetUrl = new Uri("http://localhost:30000/pithos/mex")
195
                          };
196
            _statusService.Description.Behaviors.Add(smb);
197

    
198

    
199
            _statusService.Open();
200
        }
201

    
202
        private void StopStatusService()
203
        {
204
            if (_statusService == null)
205
                return;
206

    
207
            if (_statusService.State == CommunicationState.Faulted)
208
                _statusService.Abort();
209
            else if (_statusService.State != CommunicationState.Closed)
210
                _statusService.Close();
211
            _statusService = null;
212

    
213
        }
214

    
215

    
216
        private void StartNetwork()
217
        {
218

    
219
            bool connected = NetworkListManager.IsConnectedToInternet;
220
            //If we are not connected retry later
221
            if (!connected)
222
            {
223
                Task.Factory.StartNewDelayed(10000, StartNetwork);
224
                return;
225
            }
226

    
227
            try
228
            {
229
                CloudClient.UsePithos = this.UsePithos;
230
                CloudClient.AuthenticationUrl = this.AuthenticationUrl;
231
                CloudClient.Authenticate(UserName, ApiKey);
232

    
233
                StartListening(RootPath);
234
                StartSending();
235
            }
236
            catch (Exception)
237
            {
238
                //Faild to authenticate due to network or account error
239
                //Retry after a while
240
                Task.Factory.StartNewDelayed(10000, StartNetwork);
241
            }
242
        }
243

    
244
        public bool UsePithos { get; set; }
245

    
246
        internal enum CloudActionType
247
        {
248
            Upload=0,
249
            Download,
250
            UploadUnconditional,
251
            DownloadUnconditional,
252
            DeleteLocal,
253
            DeleteCloud
254
        }
255

    
256
        internal class ListenerAction
257
        {
258
            public CloudActionType Action { get; set; }
259
            public FileInfo LocalFile { get; set; }
260
            public ObjectInfo CloudFile { get; set; }
261

    
262
            public Lazy<string> LocalHash { get; set; }
263

    
264
            public ListenerAction(CloudActionType action, FileInfo localFile, ObjectInfo cloudFile)
265
            {
266
                Action = action;
267
                LocalFile = localFile;
268
                CloudFile = cloudFile;
269
                LocalHash=new Lazy<string>(()=>Signature.CalculateHash(LocalFile.FullName),LazyThreadSafetyMode.ExecutionAndPublication);
270
            }
271
            
272
        }
273

    
274
        internal class LocalFileComparer:EqualityComparer<ListenerAction>
275
        {
276
            public override bool Equals(ListenerAction x, ListenerAction y)
277
            {
278
                if (x.Action != y.Action)
279
                    return false;
280
                if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
281
                    return false;
282
                if (x.CloudFile != null && y.CloudFile != null && !x.CloudFile.Hash.Equals(y.CloudFile.Hash))
283
                    return false;
284
                if (x.CloudFile == null ^ y.CloudFile == null ||
285
                    x.LocalFile == null ^ y.LocalFile == null)
286
                    return false;
287
                return true;
288
            }
289

    
290
            public override int GetHashCode(ListenerAction obj)
291
            {
292
                var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
293
                var hash2 = (obj.CloudFile == null) ? int.MaxValue : obj.CloudFile.Hash.GetHashCode();
294
                var hash3 = obj.Action.GetHashCode();
295
                return hash1 ^ hash2 & hash3;
296
            }
297
        }
298

    
299
        private BlockingCollection<ListenerAction> _networkActions=new BlockingCollection<ListenerAction>();
300

    
301
        private Timer timer;
302

    
303
        private void StartListening(string accountPath)
304
        {
305
            
306
            ProcessRemoteFiles(accountPath);
307

    
308
            Task.Factory.StartNew(ProcessListenerActions);
309
                        
310
        }
311

    
312
        private Task ProcessRemoteFiles(string accountPath)
313
        {
314
            Trace.TraceInformation("[LISTENER] Scheduled");    
315
            return Task.Factory.StartNewDelayed(10000)
316
                .ContinueWith(t=>CloudClient.ListObjects(PithosContainer))
317
                .ContinueWith(task =>
318
                                  {
319
                                      Trace.TraceInformation("[LISTENER] Start Processing");
320

    
321
                                      var remoteObjects = task.Result;
322
/*
323
                                      if (remoteObjects.Count == 0)
324
                                          return;
325
*/
326

    
327
                                      var pithosDir = new DirectoryInfo(accountPath);
328
                                      
329
                                      var remoteFiles = from info in remoteObjects
330
                                                    select info.Name.ToLower();
331
                                      
332
                                      var onlyLocal = from localFile in pithosDir.EnumerateFiles()
333
                                                      where !remoteFiles.Contains(localFile.Name.ToLower()) 
334
                                                      select new ListenerAction(CloudActionType.UploadUnconditional, localFile,ObjectInfo.Empty);
335

    
336

    
337

    
338
                                      var localNames = from info in pithosDir.EnumerateFiles()
339
                                                           select info.Name.ToLower();
340

    
341
                                      var onlyRemote = from upFile in remoteObjects
342
                                                       where !localNames.Contains(upFile.Name.ToLower())
343
                                                       select new ListenerAction(CloudActionType.DownloadUnconditional,new FileInfo(""), upFile);
344

    
345

    
346
                                      var commonObjects = from  upFile in remoteObjects
347
                                                            join  localFile in pithosDir.EnumerateFiles()
348
                                                                on upFile.Name.ToLower() equals localFile.Name.ToLower() 
349
                                                            select new ListenerAction(CloudActionType.Download, localFile, upFile);
350

    
351
                                      var uniques =
352
                                          onlyLocal.Union(onlyRemote).Union(commonObjects)
353
                                              .Except(_networkActions,new LocalFileComparer());
354
                                      
355
                                      _networkActions.AddFromEnumerable(uniques, false);
356

    
357
                                      Trace.TraceInformation("[LISTENER] End Processing");
358
                                      
359
                                  }
360
                ).ContinueWith(t=>
361
                {
362
                    if (t.IsFaulted)
363
                    {
364
                        Trace.TraceError("[LISTENER] Exception: {0}",t.Exception);                                           
365
                    }
366
                    else
367
                    {
368
                        Trace.TraceInformation("[LISTENER] Finished");                                           
369
                    }                    
370
                    ProcessRemoteFiles(accountPath);
371
                });
372
        }
373

    
374
        private void ProcessListenerActions()
375
        {
376
            foreach(var action in _networkActions.GetConsumingEnumerable())
377
            {
378
                Trace.TraceInformation("[ACTION] Start Processing {0}:{1}->{2}",action.Action,action.LocalFile,action.CloudFile.Name);
379
                var localFile = action.LocalFile;
380
                var cloudFile = action.CloudFile;
381
                var downloadPath = (cloudFile == null)? String.Empty
382
                                        : Path.Combine(RootPath,cloudFile.Name);
383
                try
384
                {
385
                    switch (action.Action)
386
                    {
387
                        case CloudActionType.UploadUnconditional:
388
                            UploadCloudFile(localFile.Name,localFile.Length,localFile.FullName,action.LocalHash.Value);
389
                            break;
390
                        case CloudActionType.DownloadUnconditional:
391
                            DownloadCloudFile(PithosContainer,cloudFile.Name,downloadPath);
392
                            break;
393
                        case CloudActionType.Download:
394
                            if (File.Exists(downloadPath))
395
                            {
396
                                if (cloudFile.Hash !=action.LocalHash.Value)
397
                                {
398
                                    var lastLocalTime =localFile.LastWriteTime;
399
                                    var lastUpTime =cloudFile.Last_Modified;
400
                                    if (lastUpTime <=lastLocalTime)
401
                                    {
402
                                        //Files in conflict
403
                                        StatusKeeper.SetFileOverlayStatus(downloadPath,FileOverlayStatus.Conflict);
404
                                    }
405
                                    else
406
                                        DownloadCloudFile(PithosContainer,action.CloudFile.Name,downloadPath);
407
                                }
408
                            }
409
                            else
410
                                DownloadCloudFile(PithosContainer,action.CloudFile.Name,downloadPath);
411
                            break;
412
                    }
413
                    Trace.TraceInformation("[ACTION] End Processing {0}:{1}->{2}", action.Action, action.LocalFile, action.CloudFile.Name);
414
                }
415
                catch (Exception exc)
416
                {
417
                    Trace.TraceError("[REQUEUE] {0} : {1} -> {2} due to exception\r\n{3}",
418
                                    action.Action, action.LocalFile,action.CloudFile,exc);                    
419

    
420
                    _networkActions.Add(action);
421
                }
422
            }
423
        }
424

    
425
      
426

    
427
        private void StartMonitoringFiles(string path)
428
        {
429
            _watcher = new FileSystemWatcher(path);
430
            _watcher.Changed += OnFileEvent;
431
            _watcher.Created += OnFileEvent;
432
            _watcher.Deleted += OnFileEvent;
433
            _watcher.Renamed += OnRenameEvent;
434
            _watcher.EnableRaisingEvents = true;
435

    
436
            Task.Factory.StartNew(ProcesFileEvents,_cancellationSource.Token);
437
        }
438

    
439
        private void ProcesFileEvents()
440
        {
441
            foreach (var state in _fileEvents.GetConsumingEnumerable())
442
            {
443
                try
444
                {
445
                    var networkState=StatusKeeper.GetNetworkState(state.Path);
446
                    //Skip if the file is already being downloaded or uploaded and 
447
                    //the change is create or modify
448
                    if (networkState != NetworkState.None && 
449
                        (
450
                            state.TriggeringChange==WatcherChangeTypes.Created ||
451
                            state.TriggeringChange==WatcherChangeTypes.Changed
452
                        ))
453
                        continue;
454
                    UpdateFileStatus(state);
455
                    UpdateOverlayStatus(state);
456
                    UpdateFileChecksum(state);
457
                    _uploadEvents.Add(state);
458
                }
459
                catch (OperationCanceledException exc)
460
                {
461
                    Trace.TraceError("[ERROR] File Event Processing:\r{0}", exc);
462
                    throw;
463
                }
464
                catch (Exception exc)
465
                {
466
                    Trace.TraceError("[ERROR] File Event Processing:\r{0}",exc);
467
                }
468
            }
469
        }
470

    
471
        private void StartSending()
472
        {
473
            Task.Factory.StartNew(() =>
474
                                      {
475
                                          foreach (var state in _uploadEvents.GetConsumingEnumerable())
476
                                          {
477
                                              try
478
                                              {
479
                                                  SynchToCloud(state);
480
                                              }
481
                                              catch (OperationCanceledException)
482
                                              {
483
                                                  throw;
484
                                              }
485
                                              catch(Exception ex)
486
                                              {
487
                                                  Trace.TraceError("[ERROR] Synch for {0}:\r{1}",state.FileName,ex);
488
                                              }
489
                                          }
490
                                          
491
                                      },_cancellationSource.Token);
492
        }
493

    
494

    
495
        private WorkflowState SynchToCloud(WorkflowState state)
496
        {
497
            if (state.Skip)
498
                return state;
499
            string path = state.Path.ToLower();            
500
            string fileName = Path.GetFileName(path);
501

    
502
            //Bypass deleted files, unless the status is Deleted
503
            if (!(File.Exists(path) || state.Status == FileStatus.Deleted))
504
            {
505
                state.Skip = true;
506
                this.StatusKeeper.RemoveFileOverlayStatus(path);
507
                return state;
508
            }
509

    
510
            switch(state.Status)
511
            {
512
                case FileStatus.Created:
513
                case FileStatus.Modified:
514
                    var info = new FileInfo(path);
515
                    long fileSize = info.Length;
516
                    UploadCloudFile(fileName, fileSize, path,state.Hash);
517
                    break;
518
                case FileStatus.Deleted:
519
                    DeleteCloudFile(fileName);
520
                    break;
521
                case FileStatus.Renamed:
522
                    RenameCloudFile(state);
523
                    break;
524
            }
525
            return state;
526
        }
527

    
528
        private void RenameCloudFile(WorkflowState state)
529
        {
530
            this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
531

    
532

    
533

    
534
            CloudClient.MoveObject(PithosContainer, state.OldFileName,PithosContainer, state.FileName);
535

    
536
            this.StatusKeeper.SetFileStatus(state.Path, FileStatus.Unchanged);
537
            this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
538
            Workflow.RaiseChangeNotification(state.Path);
539
        }
540

    
541
        private void DeleteCloudFile(string fileName)
542
        {
543
            Contract.Requires(!Path.IsPathRooted(fileName));
544

    
545
            this.StatusKeeper.SetFileOverlayStatus(fileName, FileOverlayStatus.Modified);
546

    
547
            CloudClient.MoveObject(PithosContainer,fileName,TrashContainer,fileName);
548
            this.StatusKeeper.ClearFileStatus(fileName);
549
            this.StatusKeeper.RemoveFileOverlayStatus(fileName);            
550
        }
551

    
552
        private void DownloadCloudFile(string container, string fileName, string localPath)
553
        {
554
            var state = StatusKeeper.GetNetworkState(fileName);
555
            //Abort if the file is already being uploaded or downloaded
556
            if (state != NetworkState.None)
557
                return;
558

    
559
            StatusKeeper.SetNetworkState(localPath,NetworkState.Downloading);
560
            CloudClient.GetObject(container, fileName, localPath)
561
            .ContinueWith(t=>
562
                CloudClient.GetObjectInfo(container,fileName))
563
            .ContinueWith(t=>
564
                StatusKeeper.StoreInfo(fileName,t.Result))
565
            .ContinueWith(t=>
566
                StatusKeeper.SetNetworkState(localPath,NetworkState.None))
567
            .Wait();
568
        }
569

    
570
        private void UploadCloudFile(string fileName, long fileSize, string path,string hash)
571
        {
572
            Contract.Requires(!Path.IsPathRooted(fileName));
573
            var state=StatusKeeper.GetNetworkState(fileName);
574
            //Abort if the file is already being uploaded or downloaded
575
            if (state != NetworkState.None)
576
                return;
577

    
578
            StatusKeeper.SetNetworkState(fileName,NetworkState.Uploading);
579
            
580
            //Even if GetObjectInfo times out, we can proceed with the upload            
581
            var info = CloudClient.GetObjectInfo(PithosContainer, fileName);
582
            Task.Factory.StartNew(() =>
583
            {
584
                if (hash != info.Hash)
585
                {
586
                    Task.Factory.StartNew(() => 
587
                        this.StatusKeeper.SetFileOverlayStatus(path, FileOverlayStatus.Modified))
588
                    .ContinueWith(t => 
589
                        CloudClient.PutObject(PithosContainer, fileName, path, hash));
590
                }
591
                else
592
                {
593
                    this.StatusKeeper.StoreInfo(path,info);
594
                }
595
            })
596
            .ContinueWith(t => 
597
                this.StatusKeeper.SetFileState(path, FileStatus.Unchanged, FileOverlayStatus.Normal))
598
                .ContinueWith(t=>
599
                    this.StatusKeeper.SetNetworkState(path,NetworkState.None))
600
            .Wait();
601
            Workflow.RaiseChangeNotification(path);
602
        }
603

    
604
        private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
605
        {
606
            {WatcherChangeTypes.Created,FileStatus.Created},
607
            {WatcherChangeTypes.Changed,FileStatus.Modified},
608
            {WatcherChangeTypes.Deleted,FileStatus.Deleted},
609
            {WatcherChangeTypes.Renamed,FileStatus.Renamed}
610
        };
611

    
612
        private WorkflowState UpdateFileStatus(WorkflowState  state)
613
        {
614
            string path = state.Path;
615
            FileStatus status = _statusDict[state.TriggeringChange];
616
            var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
617
            if (status == oldStatus)
618
            {
619
                state.Status = status;
620
                state.Skip = true;
621
                return state;
622
            }
623
            if (state.Status == FileStatus.Renamed)
624
                Workflow.ClearFileStatus(path);                
625

    
626
            state.Status = Workflow.SetFileStatus(path, status);                
627
            return state;
628
        }
629

    
630
        private WorkflowState UpdateOverlayStatus(WorkflowState state)
631
        {            
632
            if (state.Skip)
633
                return state;
634

    
635
            switch (state.Status)
636
            {
637
                case FileStatus.Created:
638
                case FileStatus.Modified:
639
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
640
                    break;
641
                case FileStatus.Deleted:
642
                    this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
643
                    break;
644
                case FileStatus.Renamed:
645
                    this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
646
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
647
                    break;
648
                case FileStatus.Unchanged:
649
                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
650
                    break;
651
            }
652

    
653
            if (state.Status==FileStatus.Deleted)
654
                Workflow.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
655
            else
656
                Workflow.RaiseChangeNotification(state.Path);
657
            return state;
658
        }
659

    
660

    
661
        private WorkflowState UpdateFileChecksum(WorkflowState state)
662
        {
663
            if (state.Skip)
664
                return state;
665

    
666
            if (state.Status == FileStatus.Deleted)
667
                return state;
668

    
669
            string path = state.Path;
670
            string hash = Signature.CalculateHash(path);
671

    
672
            StatusKeeper.UpdateFileChecksum(path, hash);
673

    
674
            state.Hash = hash;
675
            return state;
676
        }
677

    
678
       
679

    
680
        private FileSystemEventArgs CalculateSignature(FileSystemEventArgs arg)
681
        {
682
            Debug.WriteLine(String.Format("{0} {1} {2}", arg.ChangeType, arg.Name, arg.FullPath), "INFO");
683
            return arg;
684
        }
685

    
686
        void OnFileEvent(object sender, FileSystemEventArgs e)
687
        {
688
            _fileEvents.Add(new WorkflowState{Path=e.FullPath,FileName = e.Name,TriggeringChange=e.ChangeType});            
689
        }
690

    
691
        void OnRenameEvent(object sender, RenamedEventArgs e)
692
        {
693
            _fileEvents.Add(new WorkflowState { OldPath=e.OldFullPath,OldFileName=e.OldName,
694
                Path = e.FullPath, FileName = e.Name, TriggeringChange = e.ChangeType });
695
        }
696

    
697
        public void Stop()
698
        {
699
            if (_watcher != null)
700
            {
701
                _watcher.Changed -= OnFileEvent;
702
                _watcher.Created -= OnFileEvent;
703
                _watcher.Deleted -= OnFileEvent;
704
                _watcher.Renamed -= OnRenameEvent;
705
                _watcher.Dispose();
706
            }
707
            _watcher = null;
708
            _fileEvents.CompleteAdding();
709
            if (timer != null)
710
                timer.Dispose();
711
            timer = null;
712
            StopStatusService();
713
        }
714

    
715

    
716
        ~PithosMonitor()
717
        {
718
            Dispose(false);
719
        }
720

    
721
        public void Dispose()
722
        {
723
            Dispose(true);
724
            GC.SuppressFinalize(this);
725
        }
726

    
727
        protected virtual void Dispose(bool disposing)
728
        {
729
            if (disposing)
730
            {
731
                Stop();
732
            }
733
        }
734

    
735

    
736
    }
737
}