924f0e6b0a62ac8ccdf1a7abb756a9af5bf787bd
[pithos-ms-client] / trunk / Pithos.Core / Agents / FileWatcherAgent.cs
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel.Composition;
4 using System.Diagnostics;
5 using System.IO;
6 using System.Linq;
7 using System.Text;
8 using Pithos.Interfaces;
9
10 namespace Pithos.Core.Agents
11 {
12     [Export]
13     public class FileWatcherAgent
14     {
15         Agent<WorkflowState> _agent;
16         private FileSystemWatcher _watcher;
17
18         [Import]
19         public IStatusKeeper StatusKeeper { get; set; }
20         [Import]
21         public IPithosWorkflow Workflow { get; set; }
22         [Import]
23         public WorkflowAgent WorkflowAgent { get; set; }
24
25         public void Start(string path)
26         {
27             _watcher = new FileSystemWatcher(path);
28             _watcher.Changed += OnFileEvent;
29             _watcher.Created += OnFileEvent;
30             _watcher.Deleted += OnFileEvent;
31             _watcher.Renamed += OnRenameEvent;
32             _watcher.EnableRaisingEvents = true;
33
34
35             _agent = Agent<WorkflowState>.Start(inbox =>
36             {
37                 Action loop = null;
38                 loop = () =>
39                 {
40                     var message = inbox.Receive();
41                     var process = message.ContinueWith(t =>
42                     {
43                         var state = t.Result;
44                         Process(state);
45                         inbox.DoAsync(loop);
46                     });
47
48                     process.ContinueWith(t =>
49                     {
50                         inbox.DoAsync(loop);
51                         if (t.IsFaulted)
52                         {
53                             var ex = t.Exception.InnerException;
54                             if (ex is OperationCanceledException)
55                                 inbox.Stop();
56                             Trace.TraceError("[ERROR] File Event Processing:\r{0}", ex);
57                         }
58                     });
59
60                 };
61                 loop();
62             });
63         }
64
65         public bool Pause
66         {
67             get { return _watcher == null || !_watcher.EnableRaisingEvents; }
68             set
69             {
70                 if (_watcher != null)
71                     _watcher.EnableRaisingEvents = !value;                
72             }
73         }
74
75         public void Post(WorkflowState workflowState)
76         {
77             _agent.Post(workflowState);
78         }
79
80         public void Stop()
81         {
82             if (_watcher != null)
83             {
84                 _watcher.Changed -= OnFileEvent;
85                 _watcher.Created -= OnFileEvent;
86                 _watcher.Deleted -= OnFileEvent;
87                 _watcher.Renamed -= OnRenameEvent;
88                 _watcher.Dispose();
89             }
90             _watcher = null;
91
92             _agent.Stop();
93         }
94
95         void OnFileEvent(object sender, FileSystemEventArgs e)
96         {
97             _agent.Post(new WorkflowState { Path = e.FullPath, FileName = e.Name, TriggeringChange = e.ChangeType });
98         }
99
100         void OnRenameEvent(object sender, RenamedEventArgs e)
101         {
102             _agent.Post(new WorkflowState
103             {
104                 OldPath = e.OldFullPath,
105                 OldFileName = e.OldName,
106                 Path = e.FullPath,
107                 FileName = e.Name,
108                 TriggeringChange = e.ChangeType
109             });
110         }
111
112
113         private void Process(WorkflowState state)
114         {
115             var networkState = StatusKeeper.GetNetworkState(state.Path);
116             //Skip if the file is already being downloaded or uploaded and 
117             //the change is create or modify
118             if (networkState != NetworkState.None &&
119                 (
120                     state.TriggeringChange == WatcherChangeTypes.Created ||
121                     state.TriggeringChange == WatcherChangeTypes.Changed
122                 ))
123                 return;
124             UpdateFileStatus(state);
125             UpdateOverlayStatus(state);
126             UpdateFileChecksum(state);
127             WorkflowAgent.Post(state);
128         }
129
130         private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
131         {
132             {WatcherChangeTypes.Created,FileStatus.Created},
133             {WatcherChangeTypes.Changed,FileStatus.Modified},
134             {WatcherChangeTypes.Deleted,FileStatus.Deleted},
135             {WatcherChangeTypes.Renamed,FileStatus.Renamed}
136         };
137
138         private WorkflowState UpdateFileStatus(WorkflowState state)
139         {
140             string path = state.Path;
141             FileStatus status = _statusDict[state.TriggeringChange];
142             var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
143             if (status == oldStatus)
144             {
145                 state.Status = status;
146                 state.Skip = true;
147                 return state;
148             }
149             if (state.Status == FileStatus.Renamed)
150                 Workflow.ClearFileStatus(path);
151
152             state.Status = Workflow.SetFileStatus(path, status);
153             return state;
154         }
155
156         private WorkflowState UpdateOverlayStatus(WorkflowState state)
157         {
158             if (state.Skip)
159                 return state;
160
161             switch (state.Status)
162             {
163                 case FileStatus.Created:
164                 case FileStatus.Modified:
165                     this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
166                     break;
167                 case FileStatus.Deleted:
168                     this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
169                     break;
170                 case FileStatus.Renamed:
171                     this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
172                     this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
173                     break;
174                 case FileStatus.Unchanged:
175                     this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
176                     break;
177             }
178
179             if (state.Status == FileStatus.Deleted)
180                 Workflow.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
181             else
182                 Workflow.RaiseChangeNotification(state.Path);
183             return state;
184         }
185
186
187         private WorkflowState UpdateFileChecksum(WorkflowState state)
188         {
189             if (state.Skip)
190                 return state;
191
192             if (state.Status == FileStatus.Deleted)
193                 return state;
194
195             string path = state.Path;
196             string hash = Signature.CalculateMD5(path);
197
198             StatusKeeper.UpdateFileChecksum(path, hash);
199
200             state.Hash = hash;
201             return state;
202         }
203
204        
205
206
207     }
208 }