2 using System.Collections.Generic;
3 using System.ComponentModel.Composition;
4 using System.Diagnostics;
8 using Pithos.Interfaces;
11 namespace Pithos.Core.Agents
14 public class FileAgent
16 Agent<WorkflowState> _agent;
17 private FileSystemWatcher _watcher;
20 public IStatusKeeper StatusKeeper { get; set; }
22 public IPithosWorkflow Workflow { get; set; }
24 public WorkflowAgent WorkflowAgent { get; set; }
26 public string RootPath { get; private set; }
28 public void Start(string rootPath)
31 _watcher = new FileSystemWatcher(rootPath);
32 _watcher.Changed += OnFileEvent;
33 _watcher.Created += OnFileEvent;
34 _watcher.Deleted += OnFileEvent;
35 _watcher.Renamed += OnRenameEvent;
36 _watcher.EnableRaisingEvents = true;
39 _agent = Agent<WorkflowState>.Start(inbox =>
44 var message = inbox.Receive();
45 var process = message.ContinueWith(t =>
52 process.ContinueWith(t =>
57 var ex = t.Exception.InnerException;
58 if (ex is OperationCanceledException)
60 Trace.TraceError("[ERROR] File Event Processing:\r{0}", ex);
71 get { return _watcher == null || !_watcher.EnableRaisingEvents; }
75 _watcher.EnableRaisingEvents = !value;
79 public string FragmentsPath { get; set; }
81 public void Post(WorkflowState workflowState)
83 _agent.Post(workflowState);
90 _watcher.Changed -= OnFileEvent;
91 _watcher.Created -= OnFileEvent;
92 _watcher.Deleted -= OnFileEvent;
93 _watcher.Renamed -= OnRenameEvent;
101 // Enumerate all files in the Pithos directory except those in the Fragment folder
102 // and files with a .ignore extension
103 public IEnumerable<string> EnumerateFiles(string searchPattern="*")
105 var monitoredFiles = from filePath in Directory.EnumerateFileSystemEntries(RootPath, searchPattern, SearchOption.AllDirectories)
106 where !Ignore(filePath)
108 return monitoredFiles;
111 public IEnumerable<FileInfo> EnumerateFileInfos(string searchPattern="*")
113 var rootDir = new DirectoryInfo(RootPath);
114 var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
115 where !Ignore(file.FullName)
117 return monitoredFiles;
120 public IEnumerable<string> EnumerateFilesAsRelativeUrls(string searchPattern="*")
122 var rootDir = new DirectoryInfo(RootPath);
123 var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
124 where !Ignore(file.FullName)
125 select file.AsRelativeUrlTo(RootPath);
126 return monitoredFiles;
132 private bool Ignore(string filePath)
134 if (filePath.StartsWith(FragmentsPath))
139 //Post a Change message for all events except rename
140 void OnFileEvent(object sender, FileSystemEventArgs e)
142 //Ignore events that affect the Fragments folder
143 var filePath = e.FullPath;
144 if (Ignore(filePath))
146 _agent.Post(new WorkflowState { Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
150 //Post a Change message for renames containing the old and new names
151 void OnRenameEvent(object sender, RenamedEventArgs e)
153 var oldFullPath = e.OldFullPath;
154 var fullPath = e.FullPath;
155 if (Ignore(oldFullPath) || Ignore(fullPath))
158 _agent.Post(new WorkflowState
160 OldPath = oldFullPath,
161 OldFileName = e.OldName,
164 TriggeringChange = e.ChangeType
169 private void Process(WorkflowState state)
171 Debug.Assert(!Ignore(state.Path));
173 var networkState = NetworkGate.GetNetworkState(state.Path);
174 //Skip if the file is already being downloaded or uploaded and
175 //the change is create or modify
176 if (networkState != NetworkOperation.None &&
178 state.TriggeringChange == WatcherChangeTypes.Created ||
179 state.TriggeringChange == WatcherChangeTypes.Changed
182 UpdateFileStatus(state);
183 UpdateOverlayStatus(state);
184 UpdateFileChecksum(state);
185 WorkflowAgent.Post(state);
188 private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
190 {WatcherChangeTypes.Created,FileStatus.Created},
191 {WatcherChangeTypes.Changed,FileStatus.Modified},
192 {WatcherChangeTypes.Deleted,FileStatus.Deleted},
193 {WatcherChangeTypes.Renamed,FileStatus.Renamed}
196 private WorkflowState UpdateFileStatus(WorkflowState state)
198 Debug.Assert(!state.Path.Contains("fragments"));
199 Debug.Assert(!state.Path.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase));
201 string path = state.Path;
202 FileStatus status = _statusDict[state.TriggeringChange];
203 var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
204 if (status == oldStatus)
206 state.Status = status;
210 if (state.Status == FileStatus.Renamed)
211 Workflow.ClearFileStatus(path);
213 state.Status = Workflow.SetFileStatus(path, status);
217 private WorkflowState UpdateOverlayStatus(WorkflowState state)
222 switch (state.Status)
224 case FileStatus.Created:
225 case FileStatus.Modified:
226 this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
228 case FileStatus.Deleted:
229 this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
231 case FileStatus.Renamed:
232 this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
233 this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
235 case FileStatus.Unchanged:
236 this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
240 if (state.Status == FileStatus.Deleted)
241 NativeMethods.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
243 NativeMethods.RaiseChangeNotification(state.Path);
248 private WorkflowState UpdateFileChecksum(WorkflowState state)
253 if (state.Status == FileStatus.Deleted)
256 var path = state.Path;
257 //Skip calculation for folders
258 if (Directory.Exists(path))
261 string hash = Signature.CalculateMD5(path);
263 StatusKeeper.UpdateFileChecksum(path, hash);