From 4ec636f65d38f52059589267124969d0487daef0 Mon Sep 17 00:00:00 2001 From: Panagiotis Kanavos Date: Thu, 5 Jan 2012 17:20:35 +0200 Subject: [PATCH] Removed Dataflow code --- trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs | 1303 +++++++++++------------ trunk/Pithos.Core/Agents/FileAgent.cs | 126 ++- trunk/Pithos.Core/Agents/StatusAgent.cs | 50 +- trunk/Pithos.Core/Agents/WorkflowAgent.cs | 109 +- trunk/Pithos.Core/FileState.cs | 15 +- trunk/Pithos.Core/PithosMonitor.cs | 49 +- trunk/Pithos.Core/WorkflowState.cs | 17 + trunk/Pithos.Network/CloudFilesClient.cs | 5 +- trunk/Pithos.Network/RestClient.cs | 1 + 9 files changed, 884 insertions(+), 791 deletions(-) diff --git a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs index e8b9fa7..4a8c399 100644 --- a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs +++ b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs @@ -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; - + /// /// 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 /// //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges? - [Export(typeof(IShell))] - public class ShellViewModel : Screen, IStatusNotification, IShell, - IHandle, IHandle, IHandle - { + [Export(typeof(IShell))] + public class ShellViewModel : Screen, IStatusNotification, IShell, + IHandle, IHandle, IHandle + { //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 _monitors = new Dictionary(); + + private ConcurrentDictionary _monitors = new ConcurrentDictionary(); /// /// Dictionary of account monitors, keyed by account /// @@ -66,20 +66,20 @@ namespace Pithos.Client.WPF { /// // 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 Monitors - { - get { return _monitors; } - } + public ConcurrentDictionary Monitors + { + get { return _monitors; } + } /// /// The status service is used by Shell extensions to retrieve file status information /// //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"); /// /// 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 { /// /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry) /// - [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 _accounts = new ObservableConcurrentCollection(); - public ObservableConcurrentCollection 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 _recentFiles = new ObservableConcurrentCollection(); - public ObservableConcurrentCollection 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(); - _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 _accounts = new ObservableConcurrentCollection(); + public ObservableConcurrentCollection 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 _recentFiles = new ObservableConcurrentCollection(); + public ObservableConcurrentCollection 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(); + _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 iconNames = new List - { - 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 iconNames = new List + { + 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; /// /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat /// - 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 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 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); + } + } } diff --git a/trunk/Pithos.Core/Agents/FileAgent.cs b/trunk/Pithos.Core/Agents/FileAgent.cs index 39eb56d..fc5637f 100644 --- a/trunk/Pithos.Core/Agents/FileAgent.cs +++ b/trunk/Pithos.Core/Agents/FileAgent.cs @@ -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 _agent; - TransformBlock _agent; + Agent _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(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.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 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.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.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 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 diff --git a/trunk/Pithos.Core/Agents/StatusAgent.cs b/trunk/Pithos.Core/Agents/StatusAgent.cs index 1880df0..a85be0b 100644 --- a/trunk/Pithos.Core/Agents/StatusAgent.cs +++ b/trunk/Pithos.Core/Agents/StatusAgent.cs @@ -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 _persistenceAgent; - private ActionBlock _persistenceAgent; + private Agent _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(async action=> + _persistenceAgent = Agent.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; diff --git a/trunk/Pithos.Core/Agents/WorkflowAgent.cs b/trunk/Pithos.Core/Agents/WorkflowAgent.cs index ba0f3a6..bc412b1 100644 --- a/trunk/Pithos.Core/Agents/WorkflowAgent.cs +++ b/trunk/Pithos.Core/Agents/WorkflowAgent.cs @@ -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 _agent; + Agent _agent; public IStatusNotification StatusNotification { get; set; } [Import] @@ -26,44 +25,25 @@ namespace Pithos.Core.Agents [Import] public NetworkAgent NetworkAgent { get; set; } - public ActionBlock Agent - { - get { return _agent; } - } - private static readonly ILog Log = LogManager.GetLogger("WorkflowAgent"); public void Start() { - /*_agent = new ActionBlock(message => + _agent = Agent.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(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 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.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.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.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(); + 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); + } + } } diff --git a/trunk/Pithos.Core/FileState.cs b/trunk/Pithos.Core/FileState.cs index c1be0fd..41393dd 100644 --- a/trunk/Pithos.Core/FileState.cs +++ b/trunk/Pithos.Core/FileState.cs @@ -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 { + private static readonly ILog Log = LogManager.GetLogger("FileState"); + private string _filePath; private IList _tags=new List(); @@ -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) diff --git a/trunk/Pithos.Core/PithosMonitor.cs b/trunk/Pithos.Core/PithosMonitor.cs index d0a302e..da19411 100644 --- a/trunk/Pithos.Core/PithosMonitor.cs +++ b/trunk/Pithos.Core/PithosMonitor.cs @@ -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() diff --git a/trunk/Pithos.Core/WorkflowState.cs b/trunk/Pithos.Core/WorkflowState.cs index 0efb371..7d0a925 100644 --- a/trunk/Pithos.Core/WorkflowState.cs +++ b/trunk/Pithos.Core/WorkflowState.cs @@ -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; + } + } } diff --git a/trunk/Pithos.Network/CloudFilesClient.cs b/trunk/Pithos.Network/CloudFilesClient.cs index 2a112de..cf04099 100644 --- a/trunk/Pithos.Network/CloudFilesClient.cs +++ b/trunk/Pithos.Network/CloudFilesClient.cs @@ -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(); diff --git a/trunk/Pithos.Network/RestClient.cs b/trunk/Pithos.Network/RestClient.cs index 82db2d1..5b9403f 100644 --- a/trunk/Pithos.Network/RestClient.cs +++ b/trunk/Pithos.Network/RestClient.cs @@ -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; -- 1.7.10.4