Fix incorrect check in Selective Sync that prevented folder changes to propagate...
[pithos-ms-client] / trunk / Pithos.Core / Agents / FileAgent.cs
index b73e14e..b64773a 100644 (file)
 #endregion
 using System;
 using System.Collections.Generic;
-using System.ComponentModel.Composition;
-using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.IO;
 using System.Linq;
-using System.Text;
+using System.Reflection;
 using System.Threading.Tasks;
 using Pithos.Interfaces;
 using Pithos.Network;
 using log4net;
-using log4net.Core;
 
 namespace Pithos.Core.Agents
 {
 //    [Export]
     public class FileAgent
     {
+        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
         Agent<WorkflowState> _agent;
         private FileSystemWatcher _watcher;
         private FileSystemWatcherAdapter _adapter;
 
         //[Import]
         public IStatusKeeper StatusKeeper { get; set; }
+
+        public IStatusNotification StatusNotification { get; set; }
         //[Import]
         public IPithosWorkflow Workflow { get; set; }
         //[Import]
@@ -72,8 +73,49 @@ namespace Pithos.Core.Agents
         private AccountInfo AccountInfo { get; set; }
 
         internal string RootPath { get;  set; }
+        
+        private FileEventIdleBatch _eventIdleBatch;
 
-        private static readonly ILog Log = LogManager.GetLogger("FileAgent");
+        public TimeSpan IdleTimeout { get; set; }
+
+
+        private void ProcessBatchedEvents(Dictionary<string, FileSystemEventArgs[]> fileEvents)
+        {
+            StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing,String.Format("Uploading {0} files",fileEvents.Count));
+            foreach (var fileEvent in fileEvents)
+            {
+                var filePath = fileEvent.Key;
+                var changes = fileEvent.Value;
+                
+                if (Ignore(filePath)) continue;
+                                
+                foreach (var change in changes)
+                {
+                    if (change.ChangeType == WatcherChangeTypes.Renamed)
+                    {
+                        var rename = (MovedEventArgs) change;
+                        _agent.Post(new WorkflowState(change)
+                                        {
+                                            AccountInfo = AccountInfo,
+                                            OldPath = rename.OldFullPath,
+                                            OldFileName = Path.GetFileName(rename.OldName),
+                                            Path = rename.FullPath,
+                                            FileName = Path.GetFileName(rename.Name),
+                                            TriggeringChange = rename.ChangeType
+                                        });
+                    }
+                    else
+                        _agent.Post(new WorkflowState(change)
+                        {
+                            AccountInfo = AccountInfo,
+                            Path = change.FullPath,
+                            FileName = Path.GetFileName(change.Name),
+                            TriggeringChange = change.ChangeType
+                        });                        
+                }
+            }
+            StatusNotification.SetPithosStatus(PithosStatus.LocalComplete);
+        }
 
         public void Start(AccountInfo accountInfo,string rootPath)
         {
@@ -83,17 +125,22 @@ namespace Pithos.Core.Agents
                 throw new ArgumentNullException("rootPath");
             if (!Path.IsPathRooted(rootPath))
                 throw new ArgumentException("rootPath must be an absolute path","rootPath");
+            if (IdleTimeout == null)
+                throw new InvalidOperationException("IdleTimeout must have a valid value");
             Contract.EndContractBlock();
 
             AccountInfo = accountInfo;
             RootPath = rootPath;
-            _watcher = new FileSystemWatcher(rootPath) {IncludeSubdirectories = true};
+
+            _eventIdleBatch = new FileEventIdleBatch((int)IdleTimeout.TotalMilliseconds, ProcessBatchedEvents);
+
+            _watcher = new FileSystemWatcher(rootPath) {IncludeSubdirectories = true,InternalBufferSize=8*4096};
             _adapter = new FileSystemWatcherAdapter(_watcher);
 
             _adapter.Changed += OnFileEvent;
             _adapter.Created += OnFileEvent;
             _adapter.Deleted += OnFileEvent;
-            _adapter.Renamed += OnRenameEvent;
+            //_adapter.Renamed += OnRenameEvent;
             _adapter.Moved += OnMoveEvent;
             _watcher.EnableRaisingEvents = true;
 
@@ -133,6 +180,8 @@ namespace Pithos.Core.Agents
 
             try
             {
+                //StatusKeeper.EnsureFileState(state.Path);
+                
                 UpdateFileStatus(state);
                 UpdateOverlayStatus(state);
                 UpdateFileChecksum(state);
@@ -170,12 +219,14 @@ namespace Pithos.Core.Agents
 
         public string CachePath { get; set; }
 
-        private List<string> _selectivePaths = new List<string>();
+        /*private List<string> _selectivePaths = new List<string>();
         public List<string> SelectivePaths
         {
             get { return _selectivePaths; }
             set { _selectivePaths = value; }
         }
+*/
+        public Selectives Selectives { get; set; }
 
 
         public void Post(WorkflowState workflowState)
@@ -191,10 +242,6 @@ namespace Pithos.Core.Agents
         {
             if (_watcher != null)
             {
-                _watcher.Changed -= OnFileEvent;
-                _watcher.Created -= OnFileEvent;
-                _watcher.Deleted -= OnFileEvent;
-                _watcher.Renamed -= OnRenameEvent;
                 _watcher.Dispose();
             }
             _watcher = null;
@@ -264,11 +311,13 @@ namespace Pithos.Core.Agents
                 return true;
             if (_ignoreFiles.ContainsKey(filePath.ToLower()))
                 return true;
-
+            
+            //If selective sync is enabled, propagate folder events
+            if (Selectives.IsSelectiveEnabled(AccountInfo.AccountKey) && Directory.Exists(filePath))
+                return false;
             //Ignore if selective synchronization is defined, 
-            return SelectivePaths.Count > 0 
-                //And the target file is not below any of the selective paths
-                && !SelectivePaths.Any(filePath.IsAtOrDirectlyBelow);
+            //And the target file is not below any of the selective paths
+            return !Selectives.IsSelected(AccountInfo, filePath);
         }
 
 /*        private static bool FoundInRoot(string filePath, string rootPath)
@@ -322,11 +371,11 @@ namespace Pithos.Core.Agents
             var filePath = e.FullPath;
             if (Ignore(filePath)) 
                 return;
-
-            _agent.Post(new WorkflowState{AccountInfo=AccountInfo, Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
+            _eventIdleBatch.Post(e);
         }
 
 
+/*
         //Post a Change message for renames containing the old and new names
         void OnRenameEvent(object sender, RenamedEventArgs e)
         {
@@ -345,8 +394,9 @@ namespace Pithos.Core.Agents
                 TriggeringChange = e.ChangeType
             });
         }
+*/
 
-        //Post a Change message for renames containing the old and new names
+        //Post a Change message for moves containing the old and new names
         void OnMoveEvent(object sender, MovedEventArgs e)
         {
             var oldFullPath = e.OldFullPath;
@@ -354,15 +404,7 @@ namespace Pithos.Core.Agents
             if (Ignore(oldFullPath) || Ignore(fullPath))
                 return;
 
-            _agent.Post(new WorkflowState
-            {
-                AccountInfo=AccountInfo,
-                OldPath = oldFullPath,
-                OldFileName = e.OldName,
-                Path = fullPath,
-                FileName = e.Name,
-                TriggeringChange = e.ChangeType
-            });
+            _eventIdleBatch.Post(e);
         }
 
 
@@ -413,18 +455,20 @@ namespace Pithos.Core.Agents
             switch (state.Status)
             {
                 case FileStatus.Created:
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified,state.ShortHash);
+                    break;
                 case FileStatus.Modified:
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified,state.ShortHash);
                     break;
                 case FileStatus.Deleted:
                     //this.StatusAgent.RemoveFileOverlayStatus(state.Path);
                     break;
                 case FileStatus.Renamed:
                     this.StatusKeeper.ClearFileStatus(state.OldPath);
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified);
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified,state.ShortHash);
                     break;
                 case FileStatus.Unchanged:
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal);
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal,state.ShortHash);
                     break;
             }
 
@@ -450,11 +494,18 @@ namespace Pithos.Core.Agents
                 return state;
 
             var info = new FileInfo(path);
-            string hash = info.CalculateHash(StatusKeeper.BlockSize,StatusKeeper.BlockHash);
-            StatusKeeper.UpdateFileChecksum(path, hash);
 
-            state.Hash = hash;
-            return state;
+            using (StatusNotification.GetNotifier("Hashing {0}", "Finished Hashing {0}", info.Name))
+            {
+
+                var shortHash = info.ComputeShortHash();
+
+                string merkleHash = info.CalculateHash(StatusKeeper.BlockSize, StatusKeeper.BlockHash);
+                StatusKeeper.UpdateFileChecksum(path, shortHash, merkleHash);
+
+                state.Hash = merkleHash;
+                return state;
+            }
         }
 
         //Does the file exist in the container's local folder?
@@ -510,6 +561,8 @@ namespace Pithos.Core.Agents
         public void Delete(string relativePath)
         {
             var absolutePath = Path.Combine(RootPath, relativePath).ToLower();
+            if (Log.IsDebugEnabled)
+                Log.DebugFormat("Deleting {0}", absolutePath);
             if (File.Exists(absolutePath))
             {    
                 try