Removed Dataflow code
authorPanagiotis Kanavos <pkanavos@gmail.com>
Thu, 5 Jan 2012 15:20:35 +0000 (17:20 +0200)
committerPanagiotis Kanavos <pkanavos@gmail.com>
Thu, 5 Jan 2012 15:20:35 +0000 (17:20 +0200)
trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
trunk/Pithos.Core/Agents/FileAgent.cs
trunk/Pithos.Core/Agents/StatusAgent.cs
trunk/Pithos.Core/Agents/WorkflowAgent.cs
trunk/Pithos.Core/FileState.cs
trunk/Pithos.Core/PithosMonitor.cs
trunk/Pithos.Core/WorkflowState.cs
trunk/Pithos.Network/CloudFilesClient.cs
trunk/Pithos.Network/RestClient.cs

index e8b9fa7..4a8c399 100644 (file)
@@ -29,9 +29,9 @@ using Pithos.Network;
 using StatusService = Pithos.Client.WPF.Services.StatusService;
 
 namespace Pithos.Client.WPF {
-    using System.ComponentModel.Composition;
+       using System.ComponentModel.Composition;
 
-    
+       
        ///<summary>
        /// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.
        /// The shell also hosts the status service called by shell extensions to retrieve file info
@@ -44,19 +44,19 @@ namespace Pithos.Client.WPF {
        /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog
        ///</remarks>           
        //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?
-    [Export(typeof(IShell))]
-    public class ShellViewModel : Screen, IStatusNotification, IShell,
-        IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
-    {
+       [Export(typeof(IShell))]
+       public class ShellViewModel : Screen, IStatusNotification, IShell,
+               IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
+       {
                //The Status Checker provides the current synch state
                //TODO: Could we remove the status checker and use events in its place?
-        private IStatusChecker _statusChecker;
-        private IEventAggregator _events;
+               private IStatusChecker _statusChecker;
+               private IEventAggregator _events;
 
-        public PithosSettings Settings { get; private set; }        
+               public PithosSettings Settings { get; private set; }
 
-               
-        private Dictionary<string, PithosMonitor> _monitors = new Dictionary<string, PithosMonitor>();
+
+        private ConcurrentDictionary<string, PithosMonitor> _monitors = new ConcurrentDictionary<string, PithosMonitor>();
                ///<summary>
                /// Dictionary of account monitors, keyed by account
                ///</summary>
@@ -66,20 +66,20 @@ namespace Pithos.Client.WPF {
                ///</remarks>
                // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?
                // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier
-        public Dictionary<string, PithosMonitor> Monitors
-        {
-            get { return _monitors; }
-        }
+        public ConcurrentDictionary<string, PithosMonitor> Monitors
+               {
+                       get { return _monitors; }
+               }
 
 
                ///<summary>
                /// The status service is used by Shell extensions to retrieve file status information
                ///</summary>
                //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
-        private ServiceHost _statusService { get; set; }
+               private ServiceHost _statusService { get; set; }
 
                //Logging in the Pithos client is provided by log4net
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
+               private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
 
                ///<summary>
                /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings
@@ -87,662 +87,661 @@ namespace Pithos.Client.WPF {
                ///<remarks>
                /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)
                ///</remarks>
-        [ImportingConstructor]         
-        public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
-        {
-            try
-            {
+               [ImportingConstructor]          
+               public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
+               {
+                       try
+                       {
 
-                _windowManager = windowManager;
+                               _windowManager = windowManager;
                                //CHECK: Caliburn doesn't need explicit command construction
-                //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
-                _statusChecker = statusChecker;
+                               //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
+                               _statusChecker = statusChecker;
                                //The event subst
-                _events = events;
-                _events.Subscribe(this);
+                               _events = events;
+                               _events.Subscribe(this);
 
-                Settings = settings;
+                               Settings = settings;
 
-                StatusMessage = "In Synch";
+                               StatusMessage = "In Synch";
 
-                _accounts.CollectionChanged += (sender, e) =>
-                                                   {
-                                                       NotifyOfPropertyChange(() => OpenFolderCaption);
-                                                       NotifyOfPropertyChange(() => HasAccounts);
-                                                   };
+                               _accounts.CollectionChanged += (sender, e) =>
+                                                                                                  {
+                                                                                                          NotifyOfPropertyChange(() => OpenFolderCaption);
+                                                                                                          NotifyOfPropertyChange(() => HasAccounts);
+                                                                                                  };
 
-            }
-            catch (Exception exc)
-            {
-                Log.Error("Error while starting the ShellViewModel",exc);
-                throw;
-            }
-        }
+                       }
+                       catch (Exception exc)
+                       {
+                               Log.Error("Error while starting the ShellViewModel",exc);
+                               throw;
+                       }
+               }
 
 
-        protected override void OnActivate()
-        {
-            base.OnActivate();
+               protected override void OnActivate()
+               {
+                       base.OnActivate();
 
-            StartMonitoring();                    
-        }
+                       StartMonitoring();                    
+               }
 
 
-        private async Task StartMonitoring()
-        {
-            try
-            {
-                var accounts = Settings.Accounts.Select(MonitorAccount);
-                await TaskEx.WhenAll(accounts);
-                _statusService = StatusService.Start();
+               private async Task StartMonitoring()
+               {
+                       try
+                       {
+                               var accounts = Settings.Accounts.Select(MonitorAccount);
+                               await TaskEx.WhenAll(accounts);
+                               _statusService = StatusService.Start();
 
 /*
-                foreach (var account in Settings.Accounts)
-                {
-                    await MonitorAccount(account);
-                }
+                               foreach (var account in Settings.Accounts)
+                               {
+                                       await MonitorAccount(account);
+                               }
 */
-                
-            }
-            catch (AggregateException exc)
-            {
-                exc.Handle(e =>
-                {
-                    Log.Error("Error while starting monitoring", e);
-                    return true;
-                });
-                throw;
-            }
-        }
-
-        protected override void OnDeactivate(bool close)
-        {
-            base.OnDeactivate(close);
-            if (close)
-            {
-                StatusService.Stop(_statusService);
-                _statusService = null;
-            }
-        }
-
-        public Task MonitorAccount(AccountSettings account)
-        {
-            return Task.Factory.StartNew(() =>
-            {                                                
-                PithosMonitor monitor = null;
-                var accountName = account.AccountName;
-
-                if (_monitors.TryGetValue(accountName, out monitor))
-                {
-                    //If the account is active
-                    if (account.IsActive)
-                        //Start the monitor. It's OK to start an already started monitor,
-                        //it will just ignore the call                        
-                        StartMonitor(monitor).Wait();                        
-                    else
-                    {
-                        //If the account is inactive
-                        //Stop and remove the monitor
-                        RemoveMonitor(accountName);
-                    }
-                    return;
-                }
-
-                //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
-                monitor = new PithosMonitor
-                              {
-                                  UserName = accountName,
-                                  ApiKey = account.ApiKey,                                  
-                                  StatusNotification = this,
-                                  RootPath = account.RootPath
-                              };
-                //PithosMonitor uses MEF so we need to resolve it
-                IoC.BuildUp(monitor);
-
-                var appSettings = Properties.Settings.Default;
-                monitor.AuthenticationUrl = account.ServerUrl;
-
-                _monitors[accountName] = monitor;
-
-                if (account.IsActive)
-                {
-                    //Don't start a monitor if it doesn't have an account and ApiKey
-                    if (String.IsNullOrWhiteSpace(monitor.UserName) ||
-                        String.IsNullOrWhiteSpace(monitor.ApiKey))
-                        return;
-                    StartMonitor(monitor);
-                }
-            });
-        }
-
-
-        protected override void OnViewLoaded(object view)
-        {
-            UpdateStatus();
-            var window = (Window)view;            
-            TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
-            base.OnViewLoaded(view);
-        }
-
-
-        #region Status Properties
-
-        private string _statusMessage;
-        public string StatusMessage
-        {
-            get { return _statusMessage; }
-            set
-            {
-                _statusMessage = value;
-                NotifyOfPropertyChange(() => StatusMessage);
-            }
-        }
-
-        private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
-        public ObservableConcurrentCollection<AccountInfo> Accounts
-        {
-            get { return _accounts; }
-        }
-
-           public bool HasAccounts
-           {
-            get { return _accounts.Count > 0; }
-           }
-
-
-        public string OpenFolderCaption
-        {
-            get
-            {
-                return (_accounts.Count == 0)
-                        ? "No Accounts Defined"
-                        : "Open Pithos Folder";
-            }
-        }
-
-        private string _pauseSyncCaption="Pause Synching";
-        public string PauseSyncCaption
-        {
-            get { return _pauseSyncCaption; }
-            set
-            {
-                _pauseSyncCaption = value;
-                NotifyOfPropertyChange(() => PauseSyncCaption);
-            }
-        }
-
-        private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
-        public ObservableConcurrentCollection<FileEntry> RecentFiles
-        {
-            get { return _recentFiles; }
-        }
-
-
-        private string _statusIcon="../Images/Pithos.ico";
-        public string StatusIcon
-        {
-            get { return _statusIcon; }
-            set
-            {
-                //_statusIcon = value;
-                NotifyOfPropertyChange(() => StatusIcon);
-            }
-        }
-
-        #endregion
-
-        #region Commands
-
-        public void ShowPreferences()
-        {
-            Settings.Reload();
-            var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings);            
-            _windowManager.ShowDialog(preferences);
-            
-        }
-
-        public void AboutPithos()
-        {
-            var about = new AboutViewModel();
-            _windowManager.ShowWindow(about);
-        }
-
-        public void SendFeedback()
-        {
-            var feedBack =  IoC.Get<FeedbackViewModel>();
-            _windowManager.ShowWindow(feedBack);
-        }
-
-        //public PithosCommand OpenPithosFolderCommand { get; private set; }
-
-        public void OpenPithosFolder()
-        {
-            var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
-            if (account == null)
-                return;
-            Process.Start(account.RootPath);
-        }
-
-        public void OpenPithosFolder(AccountInfo account)
-        {
-            Process.Start(account.AccountPath);
-        }
-
-        
+                               
+                       }
+                       catch (AggregateException exc)
+                       {
+                               exc.Handle(e =>
+                               {
+                                       Log.Error("Error while starting monitoring", e);
+                                       return true;
+                               });
+                               throw;
+                       }
+               }
+
+               protected override void OnDeactivate(bool close)
+               {
+                       base.OnDeactivate(close);
+                       if (close)
+                       {
+                               StatusService.Stop(_statusService);
+                               _statusService = null;
+                       }
+               }
+
+               public Task MonitorAccount(AccountSettings account)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {                                                
+                               PithosMonitor monitor = null;
+                               var accountName = account.AccountName;
+
+                               if (_monitors.TryGetValue(accountName, out monitor))
+                               {
+                                       //If the account is active
+                                       if (account.IsActive)
+                                               //Start the monitor. It's OK to start an already started monitor,
+                                               //it will just ignore the call                        
+                                               StartMonitor(monitor).Wait();                        
+                                       else
+                                       {
+                                               //If the account is inactive
+                                               //Stop and remove the monitor
+                                               RemoveMonitor(accountName);
+                                       }
+                                       return;
+                               }
+
+                               //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
+                               monitor = new PithosMonitor
+                                                         {
+                                                                 UserName = accountName,
+                                                                 ApiKey = account.ApiKey,                                  
+                                                                 StatusNotification = this,
+                                                                 RootPath = account.RootPath
+                                                         };
+                               //PithosMonitor uses MEF so we need to resolve it
+                               IoC.BuildUp(monitor);
+
+                               var appSettings = Properties.Settings.Default;
+                               monitor.AuthenticationUrl = account.ServerUrl;
+
+                               _monitors[accountName] = monitor;
+
+                               if (account.IsActive)
+                               {
+                                       //Don't start a monitor if it doesn't have an account and ApiKey
+                                       if (String.IsNullOrWhiteSpace(monitor.UserName) ||
+                                               String.IsNullOrWhiteSpace(monitor.ApiKey))
+                                               return;
+                                       StartMonitor(monitor);
+                               }
+                       });
+               }
+
+
+               protected override void OnViewLoaded(object view)
+               {
+                       UpdateStatus();
+                       var window = (Window)view;            
+                       TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
+                       base.OnViewLoaded(view);
+               }
+
+
+               #region Status Properties
+
+               private string _statusMessage;
+               public string StatusMessage
+               {
+                       get { return _statusMessage; }
+                       set
+                       {
+                               _statusMessage = value;
+                               NotifyOfPropertyChange(() => StatusMessage);
+                       }
+               }
+
+               private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
+               public ObservableConcurrentCollection<AccountInfo> Accounts
+               {
+                       get { return _accounts; }
+               }
+
+               public bool HasAccounts
+               {
+                       get { return _accounts.Count > 0; }
+               }
+
+
+               public string OpenFolderCaption
+               {
+                       get
+                       {
+                               return (_accounts.Count == 0)
+                                               ? "No Accounts Defined"
+                                               : "Open Pithos Folder";
+                       }
+               }
+
+               private string _pauseSyncCaption="Pause Synching";
+               public string PauseSyncCaption
+               {
+                       get { return _pauseSyncCaption; }
+                       set
+                       {
+                               _pauseSyncCaption = value;
+                               NotifyOfPropertyChange(() => PauseSyncCaption);
+                       }
+               }
+
+               private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
+               public ObservableConcurrentCollection<FileEntry> RecentFiles
+               {
+                       get { return _recentFiles; }
+               }
+
+
+               private string _statusIcon="../Images/Pithos.ico";
+               public string StatusIcon
+               {
+                       get { return _statusIcon; }
+                       set
+                       {
+                               //_statusIcon = value;
+                               NotifyOfPropertyChange(() => StatusIcon);
+                       }
+               }
+
+               #endregion
+
+               #region Commands
+
+               public void ShowPreferences()
+               {
+                       Settings.Reload();
+                       var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings);            
+                       _windowManager.ShowDialog(preferences);
+                       
+               }
+
+               public void AboutPithos()
+               {
+                       var about = new AboutViewModel();
+                       _windowManager.ShowWindow(about);
+               }
+
+               public void SendFeedback()
+               {
+                       var feedBack =  IoC.Get<FeedbackViewModel>();
+                       _windowManager.ShowWindow(feedBack);
+               }
+
+               //public PithosCommand OpenPithosFolderCommand { get; private set; }
+
+               public void OpenPithosFolder()
+               {
+                       var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
+                       if (account == null)
+                               return;
+                       Process.Start(account.RootPath);
+               }
+
+               public void OpenPithosFolder(AccountInfo account)
+               {
+                       Process.Start(account.AccountPath);
+               }
+
+               
 /*
-        public void GoToSite()
-        {            
-            var site = Properties.Settings.Default.PithosSite;
-            Process.Start(site);            
-        }
+               public void GoToSite()
+               {            
+                       var site = Properties.Settings.Default.PithosSite;
+                       Process.Start(site);            
+               }
 */
 
-        public void GoToSite(AccountInfo account)
-        {
-            /*var site = String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri,account.Token,
-                account.UserName);*/
-            Process.Start(account.SiteUri);
-        }
-
-        public void ShowFileProperties()
-        {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath + @"\pithos");
-            var files=dir.GetFiles();
-            var r=new Random();
-            var idx=r.Next(0, files.Length);
-            ShowFileProperties(files[idx].FullName);            
-        }
-
-        public void ShowFileProperties(string filePath)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!File.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;
-
-            if (accountMonitor == null)
-                return;
-
-            var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
-
-            
-
-            var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
-            _windowManager.ShowWindow(fileProperties);
-        } 
-        
-        public void ShowContainerProperties()
-        {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath);
-            var fullName = (from folder in dir.EnumerateDirectories()
-                            where (folder.Attributes & FileAttributes.Hidden) == 0
-                            select folder.FullName).First();
-            ShowContainerProperties(fullName);            
-        }
-
-        public void ShowContainerProperties(string filePath)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!Directory.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;            
-            var info = accountMonitor.GetContainerInfo(filePath);
-
-            
-
-            var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
-            _windowManager.ShowWindow(containerProperties);
-        }
-
-        public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
-        {
-            if (currentInfo==null)
-                throw new ArgumentNullException("currentInfo");
-            Contract.EndContractBlock();
-
-            var monitor = Monitors[currentInfo.Account];
-            var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
-            return newInfo;
-        }
-
-        public ContainerInfo RefreshContainerInfo(ContainerInfo container)
-        {
-            if (container == null)
-                throw new ArgumentNullException("container");
-            Contract.EndContractBlock();
-
-            var monitor = Monitors[container.Account];
-            var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
-            return newInfo;
-        }
-
-
-        public void ToggleSynching()
-        {
-            bool isPaused=false;
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-                monitor.Pause = !monitor.Pause;
-                isPaused = monitor.Pause;
-            }
-
-            PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
-            var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
-            StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
-        }
-
-        public void ExitPithos()
-        {
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-                monitor.Stop();
-            }
-
-            ((Window)GetView()).Close();
-        }
-        #endregion
-
-
-        private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
-            {
-                new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
-                new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
-                new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
-            }.ToDictionary(s => s.Status);
-
-        readonly IWindowManager _windowManager;
+               public void GoToSite(AccountInfo account)
+               {
+                       /*var site = String.Format("{0}/ui/?token={1}&user={2}",
+                               account.SiteUri,account.Token,
+                               account.UserName);*/
+                       Process.Start(account.SiteUri);
+               }
+
+               public void ShowFileProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath + @"\pithos");
+                       var files=dir.GetFiles();
+                       var r=new Random();
+                       var idx=r.Next(0, files.Length);
+                       ShowFileProperties(files[idx].FullName);            
+               }
+
+               public void ShowFileProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!File.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                       var account = pair.Key;
+                       var accountMonitor = pair.Value;
+
+                       if (accountMonitor == null)
+                               return;
+
+                       var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
+
+                       
+
+                       var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
+                       _windowManager.ShowWindow(fileProperties);
+               } 
+               
+               public void ShowContainerProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath);
+                       var fullName = (from folder in dir.EnumerateDirectories()
+                                                       where (folder.Attributes & FileAttributes.Hidden) == 0
+                                                       select folder.FullName).First();
+                       ShowContainerProperties(fullName);            
+               }
+
+               public void ShowContainerProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!Directory.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                       var account = pair.Key;
+                       var accountMonitor = pair.Value;            
+                       var info = accountMonitor.GetContainerInfo(filePath);
+
+                       
+
+                       var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
+                       _windowManager.ShowWindow(containerProperties);
+               }
+
+               public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
+               {
+                       if (currentInfo==null)
+                               throw new ArgumentNullException("currentInfo");
+                       Contract.EndContractBlock();
+
+                       var monitor = Monitors[currentInfo.Account];
+                       var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
+                       return newInfo;
+               }
+
+               public ContainerInfo RefreshContainerInfo(ContainerInfo container)
+               {
+                       if (container == null)
+                               throw new ArgumentNullException("container");
+                       Contract.EndContractBlock();
+
+                       var monitor = Monitors[container.Account];
+                       var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
+                       return newInfo;
+               }
+
+
+               public void ToggleSynching()
+               {
+                       bool isPaused=false;
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+                               monitor.Pause = !monitor.Pause;
+                               isPaused = monitor.Pause;
+                       }
+
+                       PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
+                       var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
+                       StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
+               }
+
+               public void ExitPithos()
+               {
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+                               monitor.Stop();
+                       }
+
+                       ((Window)GetView()).Close();
+               }
+               #endregion
+
+
+               private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
+                       {
+                               new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
+                               new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
+                               new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
+                       }.ToDictionary(s => s.Status);
+
+               readonly IWindowManager _windowManager;
 
 
                ///<summary>
                /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
                ///</summary>
-        public void UpdateStatus()
-        {
-            var pithosStatus = _statusChecker.GetPithosStatus();
-
-            if (iconNames.ContainsKey(pithosStatus))
-            {
-                var info = iconNames[pithosStatus];
-                StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
-
-                Assembly assembly = Assembly.GetExecutingAssembly();                               
-                var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
-
-
-                StatusMessage = String.Format("Pithos {0}\r\n{1}", fileVersion.FileVersion,info.StatusText);
-            }
-            
-            _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
-        }
-
-
-       
-        private Task StartMonitor(PithosMonitor monitor,int retries=0)
-        {
-            return Task.Factory.StartNew(() =>
-            {
-                using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
-                {
-                    try
-                    {
-                        Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
-
-                        monitor.Start();
-                    }
-                    catch (WebException exc)
-                    {
-                        if (AbandonRetry(monitor, retries))
-                            return;
-
-                        if (IsUnauthorized(exc))
-                        {
-                            var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
-                            Log.Error(message,exc);
-                            TryAuthorize(monitor,retries).Wait();
-                        }
-                        else
-                        {
-                            TryLater(monitor, exc,retries);
-                        }
-                    }
-                    catch (Exception exc)
-                    {
-                        if (AbandonRetry(monitor, retries)) 
-                            return;
-
-                        TryLater(monitor,exc,retries);
-                    }
-                }
-            });
-        }
-
-        private bool AbandonRetry(PithosMonitor monitor, int retries)
-        {
-            if (retries > 1)
-            {
-                var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
-                                            monitor.UserName);
-                _events.Publish(new Notification
-                                    {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
-                return true;
-            }
-            return false;
-        }
-
-
-        private async Task TryAuthorize(PithosMonitor monitor,int retries)
-        {
-            _events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });
-
-            try
-            {
-
-                var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
-
-                var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
-                account.ApiKey = credentials.Password;
-                monitor.ApiKey = credentials.Password;
-                Settings.Save();
-                await TaskEx.Delay(10000);
-                StartMonitor(monitor, retries + 1);
-                NotifyOfPropertyChange(()=>Accounts);
-            }
-            catch (AggregateException exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc.InnerException);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-                return;
-            }
-            catch (Exception exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-                return;
-                
-            }
-
-        }
-
-        private static bool IsUnauthorized(WebException exc)
-        {
-            if (exc==null)
-                throw new ArgumentNullException("exc");
-            Contract.EndContractBlock();
-
-            var response = exc.Response as HttpWebResponse;
-            if (response == null)
-                return false;
-            return (response.StatusCode == HttpStatusCode.Unauthorized);
-        }
-
-        private void TryLater(PithosMonitor monitor, Exception exc,int retries)
-        {
-            var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
-            Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
-            _events.Publish(new Notification
-                                {Title = "Error", Message = message, Level = TraceLevel.Error});
-            Log.Error(message, exc);
-        }
-
-
-        public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
-        {
-            this.StatusMessage = status;
-            
-            _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
-        }
-
-        public void NotifyChangedFile(string filePath)
-        {
-            var entry = new FileEntry {FullPath=filePath};
-            IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
-            FileEntry popped;
-            while (files.Count > 5)
-                files.TryTake(out popped);
-            files.TryAdd(entry);
-        }
-
-        public void NotifyAccount(AccountInfo account)
-        {
-            if (account== null)
-                return;
-            //TODO: What happens to an existing account whose Token has changed?
-            account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri, account.Token,
-                account.UserName);
-
-            if (Accounts.All(item => item.UserName != account.UserName))
-                Accounts.TryAdd(account);
-
-        }
-
-
-        public void RemoveMonitor(string accountName)
-        {
-            if (String.IsNullOrWhiteSpace(accountName))
-                return;
-
-            var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
-            _accounts.TryRemove(accountInfo);
-
-            PithosMonitor monitor;
-            if (Monitors.TryGetValue(accountName, out monitor))
-            {
-                Monitors.Remove(accountName);
-                monitor.Stop();
-            }
-        }
-
-        public void RefreshOverlays()
-        {
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-
-                var path = monitor.RootPath;
-
-                if (String.IsNullOrWhiteSpace(path))
-                    continue;
-
-                if (!Directory.Exists(path) && !File.Exists(path))
-                    continue;
-
-                IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
-
-                try
-                {
-                    NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
-                                                 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
-                                                 pathPointer, IntPtr.Zero);
-                }
-                finally
-                {
-                    Marshal.FreeHGlobal(pathPointer);
-                }
-            }
-        }
-
-        #region Event Handlers
-        
-        public void Handle(SelectiveSynchChanges message)
-        {
-            var accountName = message.Account.AccountName;
-            PithosMonitor monitor;
-            if (_monitors.TryGetValue(accountName, out monitor))
-            {
-                monitor.AddSelectivePaths(message.Added);
-                monitor.RemoveSelectivePaths(message.Removed);
-
-            }
-            
-        }
-
-
-        public void Handle(Notification notification)
-        {
-            if (!Settings.ShowDesktopNotifications)
-                return;
-            BalloonIcon icon = BalloonIcon.None;
-            switch (notification.Level)
-            {
-                case TraceLevel.Error:
-                    icon = BalloonIcon.Error;
-                    break;
-                case TraceLevel.Info:
-                case TraceLevel.Verbose:
-                    icon = BalloonIcon.Info;
-                    break;
-                case TraceLevel.Warning:
-                    icon = BalloonIcon.Warning;
-                    break;
-                default:
-                    icon = BalloonIcon.None;
-                    break;
-            }
-
-            if (Settings.ShowDesktopNotifications)
-            {
-                var tv = (ShellView) this.GetView();
-                tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
-            }
-        }
-        #endregion
-
-        public void Handle(ShowFilePropertiesEvent message)
-        {
-            if (message == null)
-                throw new ArgumentNullException("message");
-            if (String.IsNullOrWhiteSpace(message.FileName) )
-                throw new ArgumentException("message");
-            Contract.EndContractBlock();
-
-            var fileName = message.FileName;
-
-            if (File.Exists(fileName))
-                this.ShowFileProperties(fileName);
-            else if (Directory.Exists(fileName))
-                this.ShowContainerProperties(fileName);
-        }
-    }
+               public void UpdateStatus()
+               {
+                       var pithosStatus = _statusChecker.GetPithosStatus();
+
+                       if (iconNames.ContainsKey(pithosStatus))
+                       {
+                               var info = iconNames[pithosStatus];
+                               StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
+
+                               Assembly assembly = Assembly.GetExecutingAssembly();                               
+                               var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
+
+
+                               StatusMessage = String.Format("Pithos {0}\r\n{1}", fileVersion.FileVersion,info.StatusText);
+                       }
+                       
+                       _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
+               }
+
+
+          
+               private Task StartMonitor(PithosMonitor monitor,int retries=0)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {
+                               using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
+                               {
+                                       try
+                                       {
+                                               Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
+
+                                               monitor.Start();
+                                       }
+                                       catch (WebException exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries))
+                                                       return;
+
+                                               if (IsUnauthorized(exc))
+                                               {
+                                                       var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
+                                                       Log.Error(message,exc);
+                                                       TryAuthorize(monitor,retries).Wait();
+                                               }
+                                               else
+                                               {
+                                                       TryLater(monitor, exc,retries);
+                                               }
+                                       }
+                                       catch (Exception exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries)) 
+                                                       return;
+
+                                               TryLater(monitor,exc,retries);
+                                       }
+                               }
+                       });
+               }
+
+               private bool AbandonRetry(PithosMonitor monitor, int retries)
+               {
+                       if (retries > 1)
+                       {
+                               var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
+                                                                                       monitor.UserName);
+                               _events.Publish(new Notification
+                                                                       {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
+                               return true;
+                       }
+                       return false;
+               }
+
+
+               private async Task TryAuthorize(PithosMonitor monitor,int retries)
+               {
+                       _events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });
+
+                       try
+                       {
+
+                               var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
+
+                               var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
+                               account.ApiKey = credentials.Password;
+                               monitor.ApiKey = credentials.Password;
+                               Settings.Save();
+                               await TaskEx.Delay(10000);
+                               StartMonitor(monitor, retries + 1);
+                               NotifyOfPropertyChange(()=>Accounts);
+                       }
+                       catch (AggregateException exc)
+                       {
+                               string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
+                               Log.Error(message, exc.InnerException);
+                               _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+                               return;
+                       }
+                       catch (Exception exc)
+                       {
+                               string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
+                               Log.Error(message, exc);
+                               _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+                               return;
+                               
+                       }
+
+               }
+
+               private static bool IsUnauthorized(WebException exc)
+               {
+                       if (exc==null)
+                               throw new ArgumentNullException("exc");
+                       Contract.EndContractBlock();
+
+                       var response = exc.Response as HttpWebResponse;
+                       if (response == null)
+                               return false;
+                       return (response.StatusCode == HttpStatusCode.Unauthorized);
+               }
+
+               private void TryLater(PithosMonitor monitor, Exception exc,int retries)
+               {
+                       var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
+                       Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
+                       _events.Publish(new Notification
+                                                               {Title = "Error", Message = message, Level = TraceLevel.Error});
+                       Log.Error(message, exc);
+               }
+
+
+               public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
+               {
+                       this.StatusMessage = status;
+                       
+                       _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
+               }
+
+               public void NotifyChangedFile(string filePath)
+               {
+                       var entry = new FileEntry {FullPath=filePath};
+                       IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
+                       FileEntry popped;
+                       while (files.Count > 5)
+                               files.TryTake(out popped);
+                       files.TryAdd(entry);
+               }
+
+               public void NotifyAccount(AccountInfo account)
+               {
+                       if (account== null)
+                               return;
+                       //TODO: What happens to an existing account whose Token has changed?
+                       account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
+                               account.SiteUri, account.Token,
+                               account.UserName);
+
+                       if (Accounts.All(item => item.UserName != account.UserName))
+                               Accounts.TryAdd(account);
+
+               }
+
+
+               public void RemoveMonitor(string accountName)
+               {
+                       if (String.IsNullOrWhiteSpace(accountName))
+                               return;
+
+                       var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
+                       _accounts.TryRemove(accountInfo);
+
+                       PithosMonitor monitor;
+                       if (Monitors.TryRemove(accountName, out monitor))
+                       {
+                               monitor.Stop();
+                       }
+               }
+
+               public void RefreshOverlays()
+               {
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+
+                               var path = monitor.RootPath;
+
+                               if (String.IsNullOrWhiteSpace(path))
+                                       continue;
+
+                               if (!Directory.Exists(path) && !File.Exists(path))
+                                       continue;
+
+                               IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
+
+                               try
+                               {
+                                       NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
+                                                                                                HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
+                                                                                                pathPointer, IntPtr.Zero);
+                               }
+                               finally
+                               {
+                                       Marshal.FreeHGlobal(pathPointer);
+                               }
+                       }
+               }
+
+               #region Event Handlers
+               
+               public void Handle(SelectiveSynchChanges message)
+               {
+                       var accountName = message.Account.AccountName;
+                       PithosMonitor monitor;
+                       if (_monitors.TryGetValue(accountName, out monitor))
+                       {
+                               monitor.AddSelectivePaths(message.Added);
+                               monitor.RemoveSelectivePaths(message.Removed);
+
+                       }
+                       
+               }
+
+
+               public void Handle(Notification notification)
+               {
+                       if (!Settings.ShowDesktopNotifications)
+                               return;
+                       BalloonIcon icon = BalloonIcon.None;
+                       switch (notification.Level)
+                       {
+                               case TraceLevel.Error:
+                                       icon = BalloonIcon.Error;
+                                       break;
+                               case TraceLevel.Info:
+                               case TraceLevel.Verbose:
+                                       icon = BalloonIcon.Info;
+                                       break;
+                               case TraceLevel.Warning:
+                                       icon = BalloonIcon.Warning;
+                                       break;
+                               default:
+                                       icon = BalloonIcon.None;
+                                       break;
+                       }
+
+                       if (Settings.ShowDesktopNotifications)
+                       {
+                               var tv = (ShellView) this.GetView();
+                               tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
+                       }
+               }
+               #endregion
+
+               public void Handle(ShowFilePropertiesEvent message)
+               {
+                       if (message == null)
+                               throw new ArgumentNullException("message");
+                       if (String.IsNullOrWhiteSpace(message.FileName) )
+                               throw new ArgumentException("message");
+                       Contract.EndContractBlock();
+
+                       var fileName = message.FileName;
+
+                       if (File.Exists(fileName))
+                               this.ShowFileProperties(fileName);
+                       else if (Directory.Exists(fileName))
+                               this.ShowContainerProperties(fileName);
+               }
+       }
 }
index 39eb56d..fc5637f 100644 (file)
@@ -7,7 +7,6 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using System.Threading.Tasks.Dataflow;
 using Pithos.Interfaces;
 using Pithos.Network;
 using log4net;
@@ -15,18 +14,17 @@ using log4net.Core;
 
 namespace Pithos.Core.Agents
 {
-    [Export]
+//    [Export]
     public class FileAgent
     {
-        //Agent<WorkflowState> _agent;
-        TransformBlock<WorkflowState, WorkflowState> _agent;
+        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; }
 
         private AccountInfo AccountInfo { get; set; }
@@ -35,75 +33,95 @@ namespace Pithos.Core.Agents
 
         private static readonly ILog Log = LogManager.GetLogger("FileAgent");
 
-        public FileAgent()
+        public void Start(AccountInfo accountInfo,string rootPath)
         {
-            _agent = new TransformBlock<WorkflowState, WorkflowState>(async message => 
-                await TaskEx.Run(()=>Process(message)));
+            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;
+            _watcher.Renamed += OnRenameEvent;
+            _watcher.EnableRaisingEvents = true;
+
+
+            _agent = Agent<WorkflowState>.Start(inbox =>
+            {
+                Action loop = null;
+                loop = () =>
+                {
+                    var message = inbox.Receive();
+                    var process=message.Then(Process,inbox.CancellationToken);
+
+                    inbox.LoopAsync(process,loop,ex=>
+                        Log.ErrorFormat("[ERROR] File Event Processing:\r{0}", ex));
+                };
+                loop();
+            });
         }
 
-        private WorkflowState Process(WorkflowState message)
+        private Task<object> Process(WorkflowState state)
         {
-            Debug.Assert(!Ignore(message.Path));
+            if (state==null)
+                throw new ArgumentNullException("state");
+            Contract.EndContractBlock();
+
+            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 CompletedTask<object>.Default;
 
             try
             {
-                //Skip if the file is already being downloaded or uploaded and 
-                //the change is create or modify
-                var networkState = NetworkGate.GetNetworkState(message.Path);
-                if (networkState != NetworkOperation.None &&
-                    (message.TriggeringChange == WatcherChangeTypes.Created ||
-                     message.TriggeringChange == WatcherChangeTypes.Changed))
-                    return null;
-
-                UpdateFileStatus(message);
-                UpdateOverlayStatus(message);
-                UpdateFileChecksum(message);
-                return message;
+                UpdateFileStatus(state);
+                UpdateOverlayStatus(state);
+                UpdateFileChecksum(state);
+                WorkflowAgent.Post(state);
             }
             catch (IOException exc)
             {
-                if (File.Exists(message.Path))
+                if (File.Exists(state.Path))
                 {
-                    Log.WarnFormat("File access error occured, retrying {0}\n{1}", message.Path, exc);
-                    _agent.Post(message);
+                    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}", message.Path, exc);
+                    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}",
-                               message.Path, exc);
+                Log.WarnFormat("Error occured while indexing{0}. The file will be skipped\n{1}",
+                               state.Path, exc);
             }
-
-            return null;
+            return CompletedTask<object>.Default;
         }
 
-        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();
 
-            _agent.LinkTo(WorkflowAgent.Agent);            
-
-            AccountInfo = accountInfo;
-            RootPath = rootPath;
-            _watcher = new FileSystemWatcher(rootPath);
-            _watcher.IncludeSubdirectories = true;            
-            _watcher.Changed += OnFileEvent;
-            _watcher.Created += OnFileEvent;
-            _watcher.Deleted += OnFileEvent;
-            _watcher.Renamed += OnRenameEvent;
-            _watcher.EnableRaisingEvents = true;
-           
+/*
+        private Task Process(Task<WorkflowState> action)
+        {
+            return action.ContinueWith(t => Process(t.Result));
         }
+*/
+
 
         public bool Pause
         {
@@ -147,7 +165,7 @@ namespace Pithos.Core.Agents
             _watcher = null;
 
             if (_agent!=null)
-                _agent.Complete();
+                _agent.Stop();
         }
 
         // Enumerate all files in the Pithos directory except those in the Fragment folder
index 1880df0..a85be0b 100644 (file)
@@ -7,7 +7,6 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using System.Threading.Tasks.Dataflow;
 using Castle.ActiveRecord;
 using Castle.ActiveRecord.Framework.Config;
 using Pithos.Interfaces;
@@ -25,8 +24,7 @@ namespace Pithos.Core.Agents
         [System.ComponentModel.Composition.Import]
         public IPithosSettings Settings { get; set; }
 
-        //private Agent<Action> _persistenceAgent;
-        private ActionBlock<Action> _persistenceAgent;
+        private Agent<Action> _persistenceAgent;
 
 
         private static readonly ILog Log = LogManager.GetLogger("StatusAgent");
@@ -76,16 +74,27 @@ namespace Pithos.Core.Agents
 
         public void StartProcessing(CancellationToken token)
         {
-            _persistenceAgent = new ActionBlock<Action>(async action=>
+            _persistenceAgent = Agent<Action>.Start(queue =>
             {
-                    try
-                    {
-                        await TaskEx.Run(action);
-                    }
-                    catch (Exception ex)
+                Action loop = null;
+                loop = () =>
+                {
+                    var job = queue.Receive();
+                    job.ContinueWith(t =>
                     {
-                        Log.ErrorFormat("[ERROR] STATE \n{0}",ex);
-                    }
+                        var action = job.Result;
+                        try
+                        {
+                            action();
+                        }
+                        catch (Exception ex)
+                        {
+                            Log.ErrorFormat("[ERROR] STATE \n{0}",ex);
+                        }
+                        queue.DoAsync(loop);
+                    });
+                };
+                loop();
             });
             
         }
@@ -94,7 +103,7 @@ namespace Pithos.Core.Agents
 
         public void Stop()
         {
-            _persistenceAgent.Complete();            
+            _persistenceAgent.Stop();            
         }
        
 
@@ -118,7 +127,7 @@ namespace Pithos.Core.Agents
                              select new {state.Id, state.FilePath}).ToList();
             //and check each one
             var missingStates= (from path in statePaths
-                               where !File.Exists(path.FilePath)
+                                where !File.Exists(path.FilePath) && !Directory.Exists(path.FilePath)
                                select path.Id).ToList();
             //Finally, retrieve the states that correspond to the deleted files            
             var deletedFiles = from state in fileStates 
@@ -245,7 +254,7 @@ namespace Pithos.Core.Agents
                     }
                     else
                     {
-                        state = new FileState {FilePath = filePath};
+                        state = new FileState {FilePath = filePath,Id=Guid.NewGuid()};
                         setter(state);
                         state.Save();
                     }                    
@@ -287,7 +296,8 @@ namespace Pithos.Core.Agents
                     if (state == null)
                     {
                         Log.WarnFormat("[NOFILE] Unable to set status for {0}.", filePath);
-                        return;
+                        state = new FileState { FilePath = path, Id = Guid.NewGuid() };
+                        state.Create();                        
                     }
                     setter(state);
                     state.Save();
@@ -335,8 +345,12 @@ namespace Pithos.Core.Agents
 
             try
             {
-                var state = FileState.FindByFilePath(path);
-                return state == null ? FileOverlayStatus.Unversioned : state.OverlayStatus;
+
+                //var state = FileState.FindByFilePath(path);
+                var st=from state in FileState.Queryable
+                       where state.FilePath == path.ToLower()
+                       select state.OverlayStatus; ;                
+                return st.FirstOrDefault(); // state == null ? FileOverlayStatus.Unversioned : state.OverlayStatus;
             }
             catch (Exception exc)
             {
@@ -427,6 +441,8 @@ namespace Pithos.Core.Agents
                 throw new ArgumentException("The path must be rooted", "path");
             Contract.EndContractBlock();
 
+            checked HERE to fix conflicts
+
             UpdateStatus(path.ToLower(),state=>
                                   {
                                       state.FileStatus = fileStatus;
index ba0f3a6..bc412b1 100644 (file)
@@ -7,7 +7,6 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using System.Threading.Tasks.Dataflow;
 using Pithos.Interfaces;
 using Pithos.Network;
 using log4net;
@@ -17,7 +16,7 @@ namespace Pithos.Core.Agents
     [Export]
     public class WorkflowAgent
     {
-        private ActionBlock<WorkflowState> _agent;
+        Agent<WorkflowState> _agent;
                 
         public IStatusNotification StatusNotification { get; set; }
         [Import]
@@ -26,44 +25,25 @@ namespace Pithos.Core.Agents
         [Import]
         public NetworkAgent NetworkAgent { get; set; }
 
-        public ActionBlock<WorkflowState> Agent
-        {
-            get { return _agent; }
-        }
-
         private static readonly ILog Log = LogManager.GetLogger("WorkflowAgent");
 
         public void Start()
         {
-            /*_agent = new ActionBlock<WorkflowState>(message =>
+            _agent = Agent<WorkflowState>.Start(inbox =>
             {
                 Action loop = null;
                 loop = () =>
                 {
-                    //var message = inbox.Receive();
-                    Process(message);
+                    var message = inbox.Receive();
                     var process = message.Then(Process, inbox.CancellationToken);                        
                     inbox.LoopAsync(process,loop,ex=>
                             Log.ErrorFormat("[ERROR] Synch for {0}:\r{1}", message.Result.FileName, ex));
                 };
                 loop();
-            });*/
-            _agent = new ActionBlock<WorkflowState>(async message =>
-            {
-                try
-                {
-                    var action=await TaskEx.Run(()=>Process(message));
-                    if (action!=null)
-                        NetworkAgent.Post(action);
-                }
-                catch (Exception ex)
-                {
-                    Log.ErrorFormat("[ERROR] Synch for {0}:\r{1}", message.FileName, ex);                    
-                }
-            });            
+            });
         }
 
-        private CloudAction Process(WorkflowState state)
+        private Task<object> Process(WorkflowState state)
         {
             var accountInfo = state.AccountInfo;
             using (log4net.ThreadContext.Stacks["Workflow"].Push("Process"))
@@ -74,7 +54,7 @@ namespace Pithos.Core.Agents
                 {
                     if (Log.IsDebugEnabled) Log.DebugFormat("Skipping {0}",state.FileName);
                     
-                    return null;
+                    return CompletedTask<object>.Default;
                 }
                 string path = state.Path.ToLower();
 
@@ -86,7 +66,7 @@ namespace Pithos.Core.Agents
                     
                     if (Log.IsDebugEnabled) Log.DebugFormat("Skipped missing {0}", state.FileName);
 
-                    return null;
+                    return CompletedTask<object>.Default;
                 }
 
                 var fileState = FileState.FindByFilePath(path);
@@ -97,16 +77,19 @@ namespace Pithos.Core.Agents
                 {
                     case FileStatus.Created:
                     case FileStatus.Modified:
-                        return new CloudUploadAction(accountInfo,info, fileState, accountInfo.BlockSize,
-                            accountInfo.BlockHash);
+                        NetworkAgent.Post(new CloudUploadAction(accountInfo,info, fileState, accountInfo.BlockSize,
+                            accountInfo.BlockHash));
+                        break;
                     case FileStatus.Deleted:                       
-                        return new CloudDeleteAction(accountInfo,info, fileState);
+                        NetworkAgent.Post(new CloudDeleteAction(accountInfo,info, fileState));
+                        break;
                     case FileStatus.Renamed:
-                        return new CloudMoveAction(accountInfo,CloudActionType.RenameCloud, 
-                            new FileInfo(state.OldPath),
-                            new FileInfo(state.Path));
+                        NetworkAgent.Post(new CloudMoveAction(accountInfo,CloudActionType.RenameCloud, new FileInfo(state.OldPath),
+                                                              new FileInfo(state.Path)));
+                        break;
                 }
-                return null;
+
+                return CompletedTask<object>.Default;
             }
         }
 
@@ -126,44 +109,52 @@ namespace Pithos.Core.Agents
                     .ToLower();
 
 
+
+                var account = accountInfo;
                 var pendingEntries = from state in FileState.Queryable
                                      where state.FileStatus != FileStatus.Unchanged &&
                                            !state.FilePath.StartsWith(cachePath) &&
                                            !state.FilePath.EndsWith(".ignore") &&
-                                           state.FilePath.StartsWith(accountInfo.AccountPath)
+                                           state.FilePath.StartsWith(account.AccountPath)
                                      select state;
+                var pendingStates = new List<WorkflowState>();
+                foreach (var state in pendingEntries)
+                {
+                    pendingStates.Add(new WorkflowState(account, state));
+                }
                 if (Log.IsDebugEnabled)
-                    Log.DebugFormat("Found {0} interrupted files",pendingEntries.Count());
-
-                var validEntries = from state in pendingEntries
-                                   select new WorkflowState
-                                              {
-                                                  AccountInfo=accountInfo,
-                                                  Path = state.FilePath.ToLower(),
-                                                  FileName = Path.GetFileName(state.FilePath).ToLower(),
-                                                  Hash = state.Checksum,
-                                                  Status = state.OverlayStatus == FileOverlayStatus.Unversioned
-                                                               ? FileStatus.Created
-                                                               : state.FileStatus,
-                                                  TriggeringChange =
-                                                      state.OverlayStatus == FileOverlayStatus.Unversioned
-                                                          ? WatcherChangeTypes.Created
-                                                          : WatcherChangeTypes.Changed
-                                              };
-                foreach (var entry in validEntries)
-                {                    
-                    Post(entry);
+                    Log.DebugFormat("Found {0} interrupted files", pendingStates.Count);
+
+
+                foreach (var entry in pendingStates)
+                {
+                    //Remove invalid state            
+                    if (Directory.Exists(entry.Path))
+                    {
+                        Debug.Assert(false, "State has path instead of file", entry.Path);
+                        StatusKeeper.ClearFileStatus(entry.Path);
+                        return;
+                    }
+                    else
+                        Post(entry);
                 }
             }
-        }       
+        }
+
 
-       
 
         public void Post(WorkflowState workflowState)
         {
             if (Log.IsDebugEnabled)
                 Log.DebugFormat("Posted {0} {1} {2}", workflowState.Path, workflowState.Status, workflowState.TriggeringChange);
-            Agent.Post(workflowState);
-        }
+
+            //Remove invalid state            
+            Debug.Assert(!Directory.Exists(workflowState.Path), "State has path instead of file", workflowState.Path);
+
+            Debug.Assert(workflowState.Path.StartsWith(workflowState.AccountInfo.AccountPath, StringComparison.InvariantCultureIgnoreCase), "File from wrong account posted");
+
+            _agent.Post(workflowState);
+        }     
+
     }
 }
index c1be0fd..41393dd 100644 (file)
@@ -14,6 +14,7 @@ using NHibernate.Engine;
 using Pithos.Core.Agents;
 using Pithos.Interfaces;
 using Pithos.Network;
+using log4net;
 
 namespace Pithos.Core
 {
@@ -28,6 +29,8 @@ namespace Pithos.Core
     [ActiveRecord]
     public class FileState:ActiveRecordLinqBase<FileState>
     {
+        private static readonly ILog Log = LogManager.GetLogger("FileState");
+        
         private string _filePath;
         private IList<FileTag> _tags=new List<FileTag>();
 
@@ -90,7 +93,17 @@ namespace Pithos.Core
             if (string.IsNullOrWhiteSpace(absolutePath))
                 throw new ArgumentNullException("absolutePath");
             Contract.EndContractBlock();
-            return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
+            try
+            {
+                return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
+            }
+            catch (Exception ex)
+            {
+                Log.Error(ex.ToString());
+                throw;
+            }
+                
+
         }
 
         public static void DeleteByFilePath(string absolutePath)
index d0a302e..da19411 100644 (file)
@@ -31,21 +31,52 @@ namespace Pithos.Core
         [Import]
         public IPithosSettings Settings{get;set;}
 
+        private IStatusKeeper _statusKeeper;
+
         [Import]
-        public IStatusKeeper StatusKeeper { get; set; }
+        public IStatusKeeper StatusKeeper
+        {
+            get { return _statusKeeper; }
+            set
+            {
+                _statusKeeper = value;
+                FileAgent.StatusKeeper = value;
+            }
+        }
+
+
+        private IPithosWorkflow _workflow;
 
         [Import]
-        public IPithosWorkflow Workflow { get; set; }
+        public IPithosWorkflow Workflow
+        {
+            get { return _workflow; }
+            set
+            {
+                _workflow = value;
+                FileAgent.Workflow = value;
+            }
+        }
 
         public ICloudClient CloudClient { get; set; }
 
         public IStatusNotification StatusNotification { get; set; }
 
+        //[Import]
+        public FileAgent FileAgent { get; private set; }
+
+        private WorkflowAgent _workflowAgent;
+
         [Import]
-        public FileAgent FileAgent { get; set; }
-        
-        [Import]
-        public WorkflowAgent WorkflowAgent { get; set; }
+        public WorkflowAgent WorkflowAgent
+        {
+            get { return _workflowAgent; }
+            set
+            {
+                _workflowAgent = value;
+                FileAgent.WorkflowAgent = value;
+            }
+        }
         
         [Import]
         public NetworkAgent NetworkAgent { get; set; }        
@@ -64,6 +95,8 @@ namespace Pithos.Core
         }
 
         private AccountInfo _accountInfo;
+        
+       
 
 
         private static readonly ILog Log = LogManager.GetLogger(typeof(PithosMonitor));
@@ -103,7 +136,11 @@ namespace Pithos.Core
 
         CancellationTokenSource _cancellationSource;
 
+        public PithosMonitor()
+        {
+            FileAgent = new FileAgent();
 
+        }
         private bool _started;
 
         public void Start()
index 0efb371..7d0a925 100644 (file)
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
+using Pithos.Interfaces;
 using Pithos.Network;
 
 namespace Pithos.Core
@@ -36,5 +37,21 @@ namespace Pithos.Core
         {
             
         }
+
+        public WorkflowState(AccountInfo accountInfo, FileState state)
+        {
+            AccountInfo = accountInfo;
+            Path = state.FilePath.ToLower();
+            FileName = System.IO.Path.GetFileName(state.FilePath).ToLower();
+            Hash = state.Checksum;
+            Status = state.OverlayStatus == FileOverlayStatus.Unversioned
+                         ? FileStatus.Created
+                         : state.FileStatus;
+            TriggeringChange =
+                state.OverlayStatus == FileOverlayStatus.Unversioned
+                    ? WatcherChangeTypes.Created
+                    : WatcherChangeTypes.Changed;
+        }
+
     }
 }
index 2a112de..cf04099 100644 (file)
@@ -1089,9 +1089,10 @@ namespace Pithos.Network
             client.UploadFileCompleted += (sender, args) => 
                 Log.InfoFormat("[BLOCK POST PROGRESS] Completed ");
 
-            
+            var buffer = new byte[count];
+            Buffer.BlockCopy(block,offset,buffer,0,count);
             //Send the block
-            var uploadTask = client.UploadDataTask(uri, "POST", block)
+            var uploadTask = client.UploadDataTask(uri, "POST", buffer)
             .ContinueWith(upload =>
             {
                 client.Dispose();
index 82db2d1..5b9403f 100644 (file)
@@ -90,6 +90,7 @@ namespace Pithos.Network
             TimedOut = false;
             var webRequest = base.GetWebRequest(address);            
             var request = (HttpWebRequest)webRequest;
+            request.ServicePoint.ConnectionLimit = 10;
             if (IfModifiedSince.HasValue)
                 request.IfModifiedSince = IfModifiedSince.Value;
             request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;