Modifications to delete folder contents
[pithos-ms-client] / trunk / Pithos.Core / Agents / FileAgent.cs
index f73a33f..8218cc6 100644 (file)
@@ -6,30 +6,47 @@ using System.Diagnostics.Contracts;
 using System.IO;
 using System.Linq;
 using System.Text;
+using System.Threading.Tasks;
 using Pithos.Interfaces;
 using Pithos.Network;
+using log4net;
+using log4net.Core;
 
 namespace Pithos.Core.Agents
 {
-    [Export]
+//    [Export]
     public class FileAgent
     {
         Agent<WorkflowState> _agent;
         private FileSystemWatcher _watcher;
 
-        [Import]
+        //[Import]
         public IStatusKeeper StatusKeeper { get; set; }
-        [Import]
+        //[Import]
         public IPithosWorkflow Workflow { get; set; }
-        [Import]
+        //[Import]
         public WorkflowAgent WorkflowAgent { get; set; }
 
-        public string RootPath { get; private set; }
+        private AccountInfo AccountInfo { get; set; }
 
-        public void Start(string rootPath)
+        private string RootPath { get;  set; }
+
+        private static readonly ILog Log = LogManager.GetLogger("FileAgent");
+
+        public void Start(AccountInfo accountInfo,string rootPath)
         {
+            if (accountInfo==null)
+                throw new ArgumentNullException("accountInfo");
+            if (String.IsNullOrWhiteSpace(rootPath))
+                throw new ArgumentNullException("rootPath");
+            if (!Path.IsPathRooted(rootPath))
+                throw new ArgumentException("rootPath must be an absolute path","rootPath");
+            Contract.EndContractBlock();
+
+            AccountInfo = accountInfo;
             RootPath = rootPath;
             _watcher = new FileSystemWatcher(rootPath);
+            _watcher.IncludeSubdirectories = true;            
             _watcher.Changed += OnFileEvent;
             _watcher.Created += OnFileEvent;
             _watcher.Deleted += OnFileEvent;
@@ -43,30 +60,61 @@ namespace Pithos.Core.Agents
                 loop = () =>
                 {
                     var message = inbox.Receive();
-                    var process = message.ContinueWith(t =>
-                    {
-                        var state = t.Result;
-                        Process(state);
-                        inbox.DoAsync(loop);
-                    });
-
-                    process.ContinueWith(t =>
-                    {
-                        inbox.DoAsync(loop);
-                        if (t.IsFaulted)
-                        {
-                            var ex = t.Exception.InnerException;
-                            if (ex is OperationCanceledException)
-                                inbox.Stop();
-                            Trace.TraceError("[ERROR] File Event Processing:\r{0}", ex);
-                        }
-                    });
+                    var process=message.Then(Process,inbox.CancellationToken);
 
+                    inbox.LoopAsync(process,loop,ex=>
+                        Log.ErrorFormat("[ERROR] File Event Processing:\r{0}", ex));
                 };
                 loop();
             });
         }
 
+        private Task<object> Process(WorkflowState state)
+        {
+            if (state==null)
+                throw new ArgumentNullException("state");
+            Contract.EndContractBlock();
+
+            if (Ignore(state.Path))
+                return CompletedTask<object>.Default;
+
+            var networkState = NetworkGate.GetNetworkState(state.Path);
+            //Skip if the file is already being downloaded or uploaded and 
+            //the change is create or modify
+            if (networkState != NetworkOperation.None &&
+                (
+                    state.TriggeringChange == WatcherChangeTypes.Created ||
+                    state.TriggeringChange == WatcherChangeTypes.Changed
+                ))
+                return CompletedTask<object>.Default;
+
+            try
+            {
+                UpdateFileStatus(state);
+                UpdateOverlayStatus(state);
+                UpdateFileChecksum(state);
+                WorkflowAgent.Post(state);
+            }
+            catch (IOException exc)
+            {
+                if (File.Exists(state.Path))
+                {
+                    Log.WarnFormat("File access error occured, retrying {0}\n{1}", state.Path, exc);
+                    _agent.Post(state);
+                }
+                else
+                {
+                    Log.WarnFormat("File {0} does not exist. Will be ignored\n{1}", state.Path, exc);
+                }
+            }
+            catch (Exception exc)
+            {
+                Log.WarnFormat("Error occured while indexing{0}. The file will be skipped\n{1}",
+                               state.Path, exc);
+            }
+            return CompletedTask<object>.Default;
+        }
+
         public bool Pause
         {
             get { return _watcher == null || !_watcher.EnableRaisingEvents; }
@@ -77,10 +125,22 @@ namespace Pithos.Core.Agents
             }
         }
 
-        public string FragmentsPath { get; set; }
+        public string CachePath { get; set; }
+
+        private List<string> _selectivePaths = new List<string>();
+        public List<string> SelectivePaths
+        {
+            get { return _selectivePaths; }
+            set { _selectivePaths = value; }
+        }
+
 
         public void Post(WorkflowState workflowState)
         {
+            if (workflowState == null)
+                throw new ArgumentNullException("workflowState");
+            Contract.EndContractBlock();
+
             _agent.Post(workflowState);
         }
 
@@ -96,7 +156,8 @@ namespace Pithos.Core.Agents
             }
             _watcher = null;
 
-            _agent.Stop();
+            if (_agent!=null)
+                _agent.Stop();
         }
 
         // Enumerate all files in the Pithos directory except those in the Fragment folder
@@ -132,7 +193,12 @@ namespace Pithos.Core.Agents
 
         private bool Ignore(string filePath)
         {
-            if (filePath.StartsWith(FragmentsPath))
+            var pithosPath = Path.Combine(RootPath, "pithos");
+            if (pithosPath.Equals(filePath, StringComparison.InvariantCultureIgnoreCase))
+                return true;
+            if (filePath.StartsWith(CachePath))
+                return true;
+            if (_ignoreFiles.ContainsKey(filePath.ToLower()))
                 return true;
             return false;
         }
@@ -140,11 +206,12 @@ namespace Pithos.Core.Agents
         //Post a Change message for all events except rename
         void OnFileEvent(object sender, FileSystemEventArgs e)
         {
-            //Ignore events that affect the Fragments folder
+            //Ignore events that affect the cache folder
             var filePath = e.FullPath;
             if (Ignore(filePath)) 
                 return;
-            _agent.Post(new WorkflowState { Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
+
+            _agent.Post(new WorkflowState{AccountInfo=AccountInfo, Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
         }
 
 
@@ -158,6 +225,7 @@ namespace Pithos.Core.Agents
 
             _agent.Post(new WorkflowState
             {
+                AccountInfo=AccountInfo,
                 OldPath = oldFullPath,
                 OldFileName = e.OldName,
                 Path = fullPath,
@@ -167,37 +235,6 @@ namespace Pithos.Core.Agents
         }
 
 
-        private void Process(WorkflowState state)
-        {
-            Debug.Assert(!Ignore(state.Path));            
-
-            var networkState = NetworkGate.GetNetworkState(state.Path);
-            //Skip if the file is already being downloaded or uploaded and 
-            //the change is create or modify
-            if (networkState != NetworkOperation.None &&
-                (
-                    state.TriggeringChange == WatcherChangeTypes.Created ||
-                    state.TriggeringChange == WatcherChangeTypes.Changed
-                ))
-                return;
-
-            try
-            {
-                UpdateFileStatus(state);
-                UpdateOverlayStatus(state);
-                UpdateFileChecksum(state);
-                WorkflowAgent.Post(state);
-            }
-            catch (IOException exc)
-            {
-                Trace.TraceWarning("File access error occured, retrying {0}\n{1}", state.Path, exc);
-                _agent.Post(state);
-            }
-            catch (Exception exc)
-            {
-                Trace.TraceWarning("Error occured while indexing{0. The file will be skipped}\n{1}", state.Path, exc);                
-            }
-        }
 
         private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
         {
@@ -207,13 +244,18 @@ namespace Pithos.Core.Agents
             {WatcherChangeTypes.Renamed,FileStatus.Renamed}
         };
 
+        private Dictionary<string,string> _ignoreFiles=new Dictionary<string, string>();
+
         private WorkflowState UpdateFileStatus(WorkflowState state)
         {
-            Debug.Assert(!state.Path.Contains("fragments"));
-            Debug.Assert(!state.Path.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase));
+            if (state==null)
+                throw new ArgumentNullException("state");
+            if (String.IsNullOrWhiteSpace(state.Path))
+                throw new ArgumentException("The state's Path can't be empty","state");
+            Contract.EndContractBlock();
 
-            string path = state.Path;
-            FileStatus status = _statusDict[state.TriggeringChange];
+            var path = state.Path;
+            var status = _statusDict[state.TriggeringChange];
             var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
             if (status == oldStatus)
             {
@@ -230,6 +272,10 @@ namespace Pithos.Core.Agents
 
         private WorkflowState UpdateOverlayStatus(WorkflowState state)
         {
+            if (state==null)
+                throw new ArgumentNullException("state");
+            Contract.EndContractBlock();
+
             if (state.Skip)
                 return state;
 
@@ -240,10 +286,10 @@ namespace Pithos.Core.Agents
                     this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
                     break;
                 case FileStatus.Deleted:
-                    this.StatusKeeper.RemoveFileOverlayStatus(state.Path);
+                    //this.StatusAgent.RemoveFileOverlayStatus(state.Path);
                     break;
                 case FileStatus.Renamed:
-                    this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath);
+                    this.StatusKeeper.ClearFileStatus(state.OldPath);
                     this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
                     break;
                 case FileStatus.Unchanged:
@@ -272,8 +318,8 @@ namespace Pithos.Core.Agents
             if (Directory.Exists(path))
                 return state;
 
-            string hash = Signature.CalculateMD5(path);
-
+            var info = new FileInfo(path);
+            string hash = info.CalculateHash(StatusKeeper.BlockSize,StatusKeeper.BlockHash);
             StatusKeeper.UpdateFileChecksum(path, hash);
 
             state.Hash = hash;
@@ -295,13 +341,13 @@ namespace Pithos.Core.Agents
             if (File.Exists(absolutePath))
                 return true;
             //Or a directory?
-            if (Directory.Exists(RootPath))
+            if (Directory.Exists(absolutePath))
                 return true;
             //Fail if it is neither
             return false;
         }
 
-        public FileInfo GetFileInfo(string relativePath)
+        public FileSystemInfo GetFileSystemInfo(string relativePath)
         {
             if (String.IsNullOrWhiteSpace(relativePath))
                 throw new ArgumentNullException("relativePath");
@@ -311,11 +357,38 @@ namespace Pithos.Core.Agents
             Contract.EndContractBlock();            
 
             var absolutePath = Path.Combine(RootPath, relativePath);
-            Debug.Assert(File.Exists(absolutePath),String.Format("Path {0} doesn't exist",absolutePath));
 
-            return new FileInfo(absolutePath);
+            if (Directory.Exists(absolutePath))
+                return new DirectoryInfo(absolutePath).WithProperCapitalization();
+            else
+                return new FileInfo(absolutePath).WithProperCapitalization();
             
         }
 
+        public void Delete(string relativePath)
+        {
+            var absolutePath = Path.Combine(RootPath, relativePath).ToLower();
+            if (File.Exists(absolutePath))
+            {    
+                try
+                {
+                    File.Delete(absolutePath);
+                }
+                //The file may have been deleted by another thread. Just ignore the relevant exception
+                catch (FileNotFoundException) { }
+            }
+            else if (Directory.Exists(absolutePath))
+            {
+                try
+                {
+                    Directory.Delete(absolutePath, true);
+                }
+                //The directory may have been deleted by another thread. Just ignore the relevant exception
+                catch (DirectoryNotFoundException){}                
+            }
+        
+            //_ignoreFiles[absolutePath] = absolutePath;                
+            StatusKeeper.ClearFileStatus(absolutePath);
+        }
     }
 }