-#region
-/* -----------------------------------------------------------------------
- * <copyright file="PithosSettings.cs" company="GRNet">
- *
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System.ComponentModel.Composition;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using Pithos.Client.WPF.Properties;
-using Pithos.Interfaces;
-using log4net.Appender;
-using log4net.Core;
-using log4net.Repository.Hierarchy;
-
-namespace Pithos.Client.WPF.Configuration
-{
- using System;
-
- [Export(typeof(IPithosSettings))]
- [Export]
- public class PithosSettings : IPithosSettings
- {
- private readonly Settings _settings = Settings.Default;
-
- public PithosSettings()
- {
- //Settings should already be upgraded by the time we reach this point
- Debug.Assert(!_settings.MustUpgrade);
- //_settings.Upgrade();
- }
-
- public bool UseDefaultProxy
- {
- get { return _settings.UseDefaultProxy; }
- set { _settings.UseDefaultProxy = value; }
- }
-
- public bool UseManualProxy
- {
- get { return _settings.UseManualProxy; }
- set { _settings.UseManualProxy = value; }
- }
-
- public bool UseNoProxy
- {
- get { return _settings.UseNoProxy; }
- set { _settings.UseNoProxy = value; }
- }
-
- public string PithosPath
- {
- get { return _settings.PithosPath; }
- set { _settings.PithosPath = value; }
- }
-
- /* public string PithosSite
- {
- get { return _settings.PithosSite; }
- }*/
-
-
- public string IconsPath
- {
- get { return _settings.IconPath; }
- set { _settings.IconPath = value; }
- }
-
- public string UserName
- {
- get { return _settings.UserName; }
- set { _settings.UserName = value; }
- }
-
- public string ApiKey
- {
- get { return _settings.ApiKey; }
- set { _settings.ApiKey = value; }
- }
-
- public AccountsCollection Accounts
- {
- get { return _settings.Accounts ?? (_settings.Accounts = new AccountsCollection()); }
- set { _settings.Accounts = value; }
- }
-
- public string ProxyServer
- {
- get { return _settings.ProxyServer; }
- set { _settings.ProxyServer = value; }
- }
-
- public int ProxyPort
- {
- get { return _settings.ProxyPort; }
- set { _settings.ProxyPort = value; }
- }
-
- public string ProxyUsername
- {
- get { return _settings.ProxyUsername; }
- set { _settings.ProxyUsername = value; }
- }
-
-
- public string ProxyPassword
- {
- get { return _settings.ProxyPassword; }
- set { _settings.ProxyPassword = value; }
- }
-
- public string ProxyDomain
- {
- get { return _settings.ProxyDomain; }
- set { _settings.ProxyDomain = value; }
- }
-
- public bool ProxyAuthentication
- {
-
- get { return _settings.ProxyAuthentication; }
- set { _settings.ProxyAuthentication = value; }
- }
-
-
-
- public bool ExtensionsActivated
- {
-
- get { return _settings.ExtensionsActivated; }
- set { _settings.ExtensionsActivated = value; }
- }
-
- public bool ShowDesktopNotifications
- {
- get { return _settings.ShowDesktopNotifications; }
- set { _settings.ShowDesktopNotifications = value; }
- }
-
-
- public int PollingInterval
- {
- get { return _settings.PollingInterval; }
- set
- {
- if (value <= 0)
- throw new ArgumentOutOfRangeException();
- _settings.PollingInterval = value;
- }
- }
-
- public TimeSpan StartupDelay
- {
- get { return _settings.StartupDelay; }
- set
- {
- if (value < TimeSpan.Zero)
- throw new ArgumentOutOfRangeException();
- _settings.StartupDelay = value;
- }
- }
-
-
- public bool StartOnSystemStartup
- {
- get { return _settings.StartOnSystemStartup; }
- set
- {
- _settings.StartOnSystemStartup = value;
- }
- }
-
- public byte HashingParallelism
- {
- get { return _settings.HashingParallelism; }
- set { _settings.HashingParallelism = value; }
- }
-
- public string UpdateUrl
- {
- get { return _settings.UpdateUrl; }
- }
-
- public bool UpdateDiagnostics
- {
- get { return _settings.UpdateDiagnostics; }
- }
-
- public TimeSpan UpdateCheckInterval
- {
- get { return _settings.UpdateCheckInterval; }
- }
-
- public TimeSpan FileIdleTimeout
- {
- get { return _settings.FileIdleTimeout; }
- }
-
- public bool UpdateForceCheck
- {
- get { return _settings.UpdateForceCheck; }
- }
-
- public bool DebugLoggingEnabled
- {
- get { return _settings.DebugLoggingEnabled; }
- set
- {
- _settings.DebugLoggingEnabled = value;
-
- SetDebugLevel();
- }
- }
-
- public bool IgnoreCertificateErrors
- {
- get { return _settings.IgnoreCertificateErrors; }
- set
- {
- _settings.IgnoreCertificateErrors = value;
-
- }
- }
-
- private static void SetDebugLevel()
- {
- var loggerRepository = (Hierarchy) log4net.LogManager.GetRepository();
-
- var appenders = loggerRepository.GetAppenders();
-
- var debugAppender = appenders.OfType<RollingFileAppender>()
- .FirstOrDefault(a => a.Name == "DebugFileAppender");
- if (debugAppender != null)
- {
- var pithosDataPath = PithosDataPath;
- debugAppender.File = Path.Combine(pithosDataPath, "debuglog.xml");
- debugAppender.Threshold = !Settings.Default.DebugLoggingEnabled ? Level.Off : Level.All;
- debugAppender.ActivateOptions();
- }
- }
-
- public static string PithosDataPath
- {
- get
- {
- var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- var pithosDataPath = Path.Combine(appDataPath, "GRNET\\PITHOS");
- return pithosDataPath;
- }
- }
-
- public void Save()
- {
- _settings.Save();
- Proxy.SetFromSettings(this);
- }
-
- public void Reload()
- {
- _settings.Reload();
- }
-
- public void Reset()
- {
- _settings.Reset();
- _settings.Save();
- }
- }
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="PithosSettings.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer in the documentation and/or other materials\r
+ * provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System.ComponentModel.Composition;\r
+using System.Diagnostics;\r
+using System.IO;\r
+using System.Linq;\r
+using Pithos.Client.WPF.Properties;\r
+using Pithos.Interfaces;\r
+using log4net.Appender;\r
+using log4net.Core;\r
+using log4net.Repository.Hierarchy;\r
+\r
+namespace Pithos.Client.WPF.Configuration\r
+{\r
+ using System;\r
+\r
+ [Export(typeof(IPithosSettings))]\r
+ [Export]\r
+ public class PithosSettings : IPithosSettings\r
+ {\r
+ private readonly Settings _settings = Settings.Default;\r
+\r
+ public PithosSettings()\r
+ {\r
+ //Settings should already be upgraded by the time we reach this point\r
+ Debug.Assert(!_settings.MustUpgrade);\r
+ //_settings.Upgrade();\r
+ }\r
+\r
+ public bool UseDefaultProxy\r
+ {\r
+ get { return _settings.UseDefaultProxy; }\r
+ set { _settings.UseDefaultProxy = value; }\r
+ }\r
+\r
+ public bool UseManualProxy\r
+ {\r
+ get { return _settings.UseManualProxy; }\r
+ set { _settings.UseManualProxy = value; }\r
+ }\r
+\r
+ public bool UseNoProxy\r
+ {\r
+ get { return _settings.UseNoProxy; }\r
+ set { _settings.UseNoProxy = value; }\r
+ }\r
+\r
+ public string PithosPath\r
+ {\r
+ get { return _settings.PithosPath; }\r
+ set { _settings.PithosPath = value; }\r
+ }\r
+\r
+ /* public string PithosSite\r
+ {\r
+ get { return _settings.PithosSite; }\r
+ }*/\r
+\r
+\r
+ public string IconsPath\r
+ {\r
+ get { return _settings.IconPath; }\r
+ set { _settings.IconPath = value; }\r
+ }\r
+\r
+ public string UserName\r
+ {\r
+ get { return _settings.UserName; }\r
+ set { _settings.UserName = value; }\r
+ }\r
+\r
+ public string ApiKey\r
+ {\r
+ get { return _settings.ApiKey; }\r
+ set { _settings.ApiKey = value; }\r
+ }\r
+\r
+ public AccountsCollection Accounts\r
+ {\r
+ get { return _settings.Accounts ?? (_settings.Accounts = new AccountsCollection()); }\r
+ set { _settings.Accounts = value; }\r
+ }\r
+\r
+ public string ProxyServer\r
+ {\r
+ get { return _settings.ProxyServer; }\r
+ set { _settings.ProxyServer = value; }\r
+ }\r
+\r
+ public int ProxyPort\r
+ {\r
+ get { return _settings.ProxyPort; }\r
+ set { _settings.ProxyPort = value; }\r
+ }\r
+\r
+ public string ProxyUsername\r
+ {\r
+ get { return _settings.ProxyUsername; }\r
+ set { _settings.ProxyUsername = value; }\r
+ }\r
+\r
+\r
+ public string ProxyPassword\r
+ {\r
+ get { return _settings.ProxyPassword; }\r
+ set { _settings.ProxyPassword = value; }\r
+ }\r
+\r
+ public string ProxyDomain\r
+ {\r
+ get { return _settings.ProxyDomain; }\r
+ set { _settings.ProxyDomain = value; }\r
+ }\r
+\r
+ public bool ProxyAuthentication\r
+ {\r
+\r
+ get { return _settings.ProxyAuthentication; }\r
+ set { _settings.ProxyAuthentication = value; }\r
+ }\r
+\r
+ \r
+ \r
+ public bool ExtensionsActivated\r
+ {\r
+\r
+ get { return _settings.ExtensionsActivated; }\r
+ set { _settings.ExtensionsActivated = value; }\r
+ }\r
+\r
+ public bool ShowDesktopNotifications\r
+ {\r
+ get { return _settings.ShowDesktopNotifications; }\r
+ set { _settings.ShowDesktopNotifications = value; }\r
+ }\r
+\r
+\r
+ public int PollingInterval\r
+ {\r
+ get { return _settings.PollingInterval; }\r
+ set\r
+ {\r
+ if (value <= 0)\r
+ throw new ArgumentOutOfRangeException();\r
+ _settings.PollingInterval = value;\r
+ }\r
+ }\r
+\r
+ public TimeSpan StartupDelay\r
+ {\r
+ get { return _settings.StartupDelay; }\r
+ set\r
+ {\r
+ if (value < TimeSpan.Zero)\r
+ throw new ArgumentOutOfRangeException();\r
+ _settings.StartupDelay = value;\r
+ }\r
+ }\r
+\r
+\r
+ public bool StartOnSystemStartup\r
+ {\r
+ get { return _settings.StartOnSystemStartup; }\r
+ set\r
+ {\r
+ _settings.StartOnSystemStartup = value;\r
+ }\r
+ }\r
+\r
+ public byte HashingParallelism\r
+ {\r
+ get { return _settings.HashingParallelism; }\r
+ set { _settings.HashingParallelism = value; }\r
+ }\r
+\r
+ public string UpdateUrl\r
+ {\r
+ get { return _settings.UpdateUrl; }\r
+ }\r
+\r
+ public bool UpdateDiagnostics\r
+ {\r
+ get { return _settings.UpdateDiagnostics; }\r
+ }\r
+\r
+ public TimeSpan UpdateCheckInterval\r
+ {\r
+ get { return _settings.UpdateCheckInterval; }\r
+ }\r
+\r
+ public TimeSpan FileIdleTimeout\r
+ {\r
+ get { return _settings.FileIdleTimeout; }\r
+ }\r
+\r
+ public bool UpdateForceCheck\r
+ {\r
+ get { return _settings.UpdateForceCheck; }\r
+ }\r
+\r
+ public bool DebugLoggingEnabled\r
+ {\r
+ get { return _settings.DebugLoggingEnabled; }\r
+ set\r
+ {\r
+ _settings.DebugLoggingEnabled = value;\r
+\r
+ SetDebugLevel();\r
+ }\r
+ }\r
+\r
+ public bool IgnoreCertificateErrors\r
+ {\r
+ get { return _settings.IgnoreCertificateErrors; }\r
+ set\r
+ {\r
+ _settings.IgnoreCertificateErrors = value;\r
+\r
+ }\r
+ }\r
+\r
+ private static void SetDebugLevel()\r
+ {\r
+ var loggerRepository = (Hierarchy) log4net.LogManager.GetRepository();\r
+\r
+ var appenders = loggerRepository.GetAppenders();\r
+\r
+ var debugAppender = appenders.OfType<RollingFileAppender>()\r
+ .FirstOrDefault(a => a.Name == "DebugFileAppender");\r
+ if (debugAppender != null)\r
+ {\r
+ var pithosDataPath = PithosDataPath;\r
+ debugAppender.File = Path.Combine(pithosDataPath, "debuglog.xml");\r
+ debugAppender.Threshold = !Settings.Default.DebugLoggingEnabled ? Level.Off : Level.All;\r
+ debugAppender.ActivateOptions();\r
+ }\r
+ }\r
+\r
+ public static string PithosDataPath\r
+ {\r
+ get\r
+ {\r
+ var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r
+ var pithosDataPath = Path.Combine(appDataPath, "GRNET\\PITHOS");\r
+ return pithosDataPath;\r
+ }\r
+ }\r
+\r
+ public void Save()\r
+ {\r
+ LockedSave();\r
+ Proxy.SetFromSettings(this);\r
+ }\r
+\r
+ private void LockedSave()\r
+ {\r
+ lock (this)\r
+ {\r
+ _settings.Save();\r
+ }\r
+ }\r
+\r
+ public void Reload()\r
+ {\r
+ _settings.Reload();\r
+ }\r
+\r
+ public void Reset()\r
+ {\r
+ _settings.Reset();\r
+ LockedSave();\r
+ }\r
+ }\r
+}\r
\r
private void DeleteCloudFile(CloudUploadAction action)\r
{\r
- using (StatusNotification.GetNotifier("Deleting server {0}", "Deleted server {0}", Path.GetFileName(action.LocalFile.Name)))\r
+ using (StatusNotification.GetNotifier("Deleting server {0}", "Deleted server {0}", true,Path.GetFileName(action.LocalFile.Name)))\r
{\r
\r
StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Deleted,\r
return new Notifier(this, startNotification, endNotification);\r
}*/\r
\r
- public Notifier GetNotifier(string startMessage, string endMessage, params object[] args)\r
+ public Notifier GetNotifier(string startMessage, string endMessage, bool isActive=true,params object[] args)\r
{\r
- return new Notifier(this, \r
+ return isActive?new Notifier(this, \r
new StatusNotification(String.Format(startMessage,args)), \r
- new StatusNotification(String.Format(endMessage,args)));\r
+ new StatusNotification(String.Format(endMessage,args)))\r
+ :new Notifier(this,(Notification) null,null);\r
}\r
\r
\r
_deleteAgent.Post(action);\r
}\r
\r
- public void DeleteCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile)\r
+ public async void DeleteCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile)\r
{\r
if (accountInfo == null)\r
throw new ArgumentNullException("accountInfo");\r
\r
var client = new CloudFilesClient(accountInfo);\r
\r
- client.DeleteObject(account, container, cloudFile.Name,cloudFile.IsDirectory);\r
+ await client.DeleteObject(account, container, cloudFile.Name,cloudFile.IsDirectory);\r
\r
StatusKeeper.ClearFileStatus(fullPath);\r
StatusNotification.Notify(new CloudNotification{Data=cloudFile});\r
\r
TreeHash localTreeHash;\r
\r
- using (StatusNotification.GetNotifier("Hashing for Download {0}", "Hashed for Download {0}", fileName))\r
+ using (StatusNotification.GetNotifier("Hashing for Download {0}", "Hashed for Download {0}", (info.Exists && info is FileInfo),fileName))\r
{\r
var state = StatusKeeper.GetStateByFilePath(filePath);\r
var progress = new Progress<HashProgress>(d =>\r
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="IdleBatch.cs" company="GRNet">
- *
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks.Dataflow;
-using System;
-using log4net;
-
-namespace Pithos.Core.Agents
-{
-
- /// <summary>
- /// Calls a callback function if an idle timeout expires after the last post
- /// </summary>
- public class IdleBatch<T>
- {
- private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- readonly Timer _triggerTimer;
-
- private readonly BatchBlock<T> _batchBlock;
- private readonly TransformBlock<T, T> _timerBlock;
- private readonly ActionBlock<T[]> _processBlock;
-
- private readonly int _timeOut;
-
- public IdleBatch(int idleTimeout, Action<T[]> action)
- {
- _timeOut = idleTimeout;
- _timerBlock = new TransformBlock<T, T>(t =>
- {
- _triggerTimer.Change(_timeOut, Timeout.Infinite);
- return t;
- });
- _batchBlock = new BatchBlock<T>(9999);
- _processBlock = new ActionBlock<T[]>(action);
-
- _timerBlock.LinkTo(_batchBlock);
- _batchBlock.LinkTo(_processBlock);
-
- _timerBlock.Completion.ContinueWith(_ => _batchBlock.Complete());
- _batchBlock.Completion.ContinueWith(_ => _processBlock.Complete());
-
- _triggerTimer = new Timer(_ => _batchBlock.TriggerBatch());
- }
-
- public void Post(T input)
- {
- _timerBlock.Post(input);
- }
-
- public int Count
- {
- get { return _timerBlock.InputCount; }
- }
-
- public void ForceTrigger()
- {
- _batchBlock.TriggerBatch();
- _timerBlock.Complete();
- _processBlock.Completion.Wait();
- }
- }
-
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="IdleBatch.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer in the documentation and/or other materials\r
+ * provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+\r
+using System.Reflection;\r
+using System.Threading;\r
+using System.Threading.Tasks.Dataflow;\r
+using System;\r
+using log4net;\r
+\r
+namespace Pithos.Core.Agents\r
+{\r
+\r
+ /// <summary>\r
+ /// Calls a callback function if an idle timeout expires after the last post\r
+ /// </summary>\r
+ public class IdleBatch<T>\r
+ {\r
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+ readonly Timer _triggerTimer;\r
+\r
+ private readonly BatchBlock<T> _batchBlock;\r
+ //private readonly TransformBlock<T, T> _timerBlock;\r
+ private readonly ActionBlock<T[]> _processBlock;\r
+\r
+ private readonly int _timeOut;\r
+\r
+ private int _eventCount = 0;\r
+\r
+ private int _elapsedSeconds = 0;\r
+\r
+ private int _timeoutSeconds;\r
+\r
+ public IdleBatch(int idleTimeout, Action<T[]> action)\r
+ {\r
+ _timeOut = idleTimeout;\r
+ /* _timerBlock = new TransformBlock<T, T>(t =>\r
+ { \r
+ //_triggerTimer.Change(_timeOut, Timeout.Infinite);\r
+ return t;\r
+ });*/\r
+ _batchBlock = new BatchBlock<T>(99999);\r
+ _processBlock = new ActionBlock<T[]>(action);\r
+\r
+ //_timerBlock.LinkTo(_batchBlock);\r
+ _batchBlock.LinkTo(_processBlock);\r
+\r
+ //_timerBlock.Completion.ContinueWith(_ => _batchBlock.Complete());\r
+ _batchBlock.Completion.ContinueWith(_ => _processBlock.Complete());\r
+\r
+ _timeoutSeconds = idleTimeout/2000;\r
+\r
+ _triggerTimer = new Timer(_ =>OnTimer(), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); \r
+ }\r
+\r
+ private void OnTimer()\r
+ {\r
+ \r
+ //If there were any events, restart counting\r
+ if ( 0!= Interlocked.Exchange(ref _eventCount, 0))\r
+ {\r
+ _elapsedSeconds = 0;\r
+ return;\r
+ }\r
+\r
+ \r
+ //Once we reach the target idle seconds, trigger\r
+ if (Interlocked.CompareExchange(ref _elapsedSeconds,0,10)>=10)\r
+ { \r
+ _batchBlock.TriggerBatch();\r
+ }\r
+ else\r
+ {\r
+ Interlocked.Increment(ref _elapsedSeconds);\r
+ }\r
+ }\r
+\r
+ public void Post(T input)\r
+ {\r
+ Interlocked.Add(ref _eventCount, 1); \r
+ _batchBlock.SendAsync(input);\r
+ }\r
+\r
+ public int Count\r
+ {\r
+ get { return _batchBlock.OutputCount; }\r
+ }\r
+\r
+ public void ForceTrigger()\r
+ {\r
+ _batchBlock.TriggerBatch();\r
+ _batchBlock.Complete();\r
+ _processBlock.Completion.Wait();\r
+ }\r
+ }\r
+\r
+}\r
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Pithos.Core
-{
- public class Notifier:IDisposable
- {
- private readonly IStatusNotification _statusNotification;
-
- private readonly Notification _endNotification;
-
- public Notifier(IStatusNotification statusNotification,string startMessage,string endMessage)
- :this(statusNotification,new Notification{Message=startMessage},new Notification{Message=endMessage} )
- {
-
- }
-
- public Notifier(IStatusNotification statusNotification,Notification startNotification,Notification endNotification)
- {
- _statusNotification = statusNotification;
- _endNotification = endNotification;
- _statusNotification.Notify(startNotification);
- }
-
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ~Notifier()
- {
- Dispose(false);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _statusNotification.Notify(_endNotification);
- }
- }
- }
-}
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace Pithos.Core\r
+{\r
+ public class Notifier:IDisposable\r
+ {\r
+ private readonly IStatusNotification _statusNotification;\r
+\r
+ private readonly Notification _endNotification;\r
+\r
+ public Notifier(IStatusNotification statusNotification,string startMessage,string endMessage)\r
+ :this(statusNotification,new Notification{Message=startMessage},new Notification{Message=endMessage} )\r
+ {\r
+ \r
+ }\r
+\r
+ public Notifier(IStatusNotification statusNotification,Notification startNotification,Notification endNotification)\r
+ {\r
+ _statusNotification = statusNotification;\r
+ _endNotification = endNotification;\r
+ \r
+ if (startNotification!=null)\r
+ _statusNotification.Notify(startNotification);\r
+ }\r
+\r
+\r
+ public void Dispose()\r
+ {\r
+ Dispose(true);\r
+ GC.SuppressFinalize(this);\r
+ }\r
+\r
+ ~Notifier()\r
+ {\r
+ Dispose(false);\r
+ }\r
+\r
+ protected virtual void Dispose(bool disposing)\r
+ {\r
+ if (disposing)\r
+ {\r
+ if (_endNotification!=null)\r
+ _statusNotification.Notify(_endNotification); \r
+ }\r
+ }\r
+ }\r
+}\r
//Server file doesn't exist\r
//deleteObjectFromLocal()\r
using (\r
- StatusNotification.GetNotifier("Deleting local {0}", "Deleted local {0}",\r
+ StatusNotification.GetNotifier("Deleting local {0}", "Deleted local {0}",true,\r
localInfo.Name))\r
{\r
DeleteLocalFile(agent, localFilePath);\r
//downloadServerObject() // Result: L = S\r
//If the file has moved on the server, move it locally before downloading\r
using (\r
- StatusNotification.GetNotifier("Downloading {0}", "Downloaded {0}",\r
+ StatusNotification.GetNotifier("Downloading {0}", "Downloaded {0}",true,\r
localInfo.Name))\r
{\r
var targetPath = MoveForServerMove(accountInfo, tuple);\r
accountInfo.BlockSize, accountInfo.BlockHash,\r
"Poll", isUnselectedRootFolder, token, progress,tuple.Merkle); \r
\r
- using (StatusNotification.GetNotifier("Uploading {0}", "Uploaded {0}",\r
+ using (StatusNotification.GetNotifier("Uploading {0}", "Uploaded {0}",true,\r
localInfo.Name))\r
{\r
await NetworkAgent.Uploader.UploadCloudFile(action, token).ConfigureAwait(false);\r
//TODO: SImplify these multiple conversions from and to Uris\r
var oldName = tuple.OldFullPath.AsRelativeTo(containerPath);\r
//Then execute a move instead of an upload\r
- using (StatusNotification.GetNotifier("Moving {0}", "Moved {0}", tuple.FileInfo.Name))\r
+ using (StatusNotification.GetNotifier("Moving {0}", "Moved {0}", true,tuple.FileInfo.Name))\r
{\r
client.MoveObject(objectInfo.Account, objectInfo.Container, oldName.ToEscapedUri(),\r
objectInfo.Container, objectInfo.Name);\r
return serverPath;\r
}\r
\r
- using (StatusNotification.GetNotifier("Moving local {0}", "Moved local {0}", Path.GetFileName(tuple.FilePath)))\r
+ using (StatusNotification.GetNotifier("Moving local {0}", "Moved local {0}", true,Path.GetFileName(tuple.FilePath)))\r
using(NetworkGate.Acquire(tuple.FilePath,NetworkOperation.Renaming))\r
{\r
\r
\r
private void DeleteCloudFile(AccountInfo accountInfo, StateTuple tuple)\r
{\r
- using (StatusNotification.GetNotifier("Deleting server {0}", "Deleted server {0}", Path.GetFileName(tuple.FilePath)))\r
+ using (StatusNotification.GetNotifier("Deleting server {0}", "Deleted server {0}", true,Path.GetFileName(tuple.FilePath)))\r
{\r
\r
StatusKeeper.SetFileState(tuple.FilePath, FileStatus.Deleted,\r
private void ProcessFile(ISession session,int total, System.Tuple<FileInfo, FileState> pair)\r
{\r
var idx = Interlocked.Increment(ref i);\r
- using (StatusNotification.GetNotifier("Indexing file {0} of {1}", "Indexed file {0} of {1} ", idx, total))\r
+ using (StatusNotification.GetNotifier("Indexing file {0} of {1}", "Indexed file {0} of {1} ", true,idx, total))\r
{\r
var fileState = pair.Item2;\r
var file = pair.Item1;\r
action.FileState = StatusKeeper.GetStateByFilePath(fileInfo.FullName);\r
\r
TreeHash localTreeHash;\r
- using (StatusNotification.GetNotifier("Merkle Hashing for Upload {0}", "Merkle Hashed for Upload {0}", fileInfo.Name))\r
+ var isLarge = ((fileInfo as FileInfo).NullSafe(f => f.Length) >= action.AccountInfo.BlockSize);\r
+ using (StatusNotification.GetNotifier("Merkle Hashing for Upload {0}", "Merkle Hashed for Upload {0}",isLarge, fileInfo.Name))\r
{\r
//TODO: Load the stored treehash if appropriate\r
//TODO: WHO updates LastMD5?\r
//If this is a read-only file, do not upload changes\r
if ( !cloudInfo.IsWritable(action.AccountInfo.UserName) ||\r
//If the file is new, but we can't upload it\r
- (!cloudInfo.Exists && !client.CanUpload(account, cloudFile)) )\r
+ (!cloudInfo.Exists && !await client.CanUpload(account, cloudFile)) )\r
{\r
MakeFileReadOnly(fullFileName);\r
StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
\r
string topHash;\r
TreeHash treeHash;\r
+ isLarge=(fileInfo as FileInfo).NullSafe(f => f.Length) >= action.AccountInfo.BlockSize;\r
using (\r
- StatusNotification.GetNotifier("Hashing {0} for Upload", "Finished hashing {0}",\r
- fileInfo.Name))\r
+ StatusNotification.GetNotifier("Hashing {0} for Upload", "Finished hashing {0}",isLarge,fileInfo.Name))\r
{\r
treeHash = localTreeHash ?? action.TreeHash.Value;\r
topHash = treeHash.TopHash.ToHashString();\r
void SetPithosStatus(PithosStatus status);\r
void SetPithosStatus(PithosStatus localSyncing, string format);\r
//Notifier GetNotifier(Notification startNotification, Notification endNotification);\r
- Notifier GetNotifier(string startMessage, string endMessage,params object[] args);\r
+ Notifier GetNotifier(string startMessage, string endMessage,bool isActive=true,params object[] args);\r
}\r
\r
public class Notification\r
<HintPath>..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll</HintPath>\r
</Reference>\r
<Reference Include="System.ServiceModel" />\r
- <Reference Include="System.Threading.Tasks.Dataflow">\r
- <HintPath>..\Libraries\System.Threading.Tasks.Dataflow.dll</HintPath>\r
+ <Reference Include="System.Threading.Tasks.Dataflow, Version=0.0.4098.29463, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">\r
+ <SpecificVersion>False</SpecificVersion>\r
+ <HintPath>bin\x86\Debug\System.Threading.Tasks.Dataflow.dll</HintPath>\r
</Reference>\r
<Reference Include="System.Transactions" />\r
<Reference Include="System.Xml.Linq" />\r
var targetUri = GetTargetUri(account).Combine(container).Combine(objectName);\r
try\r
{\r
- using (var response = await _baseHttpClient.HeadAsyncWithRetries(targetUri, 3))\r
+ using (var response = await _baseHttpClient.HeadAsyncWithRetries(targetUri, 3,true))\r
{\r
switch (response.StatusCode)\r
{\r
Account = account,\r
Container = container,\r
Name = objectName,\r
- ETag = response.Headers.ETag.Tag,\r
+ ETag = response.Headers.ETag.NullSafe(e=>e.Tag),\r
UUID = response.Headers.GetFirstValue("X-Object-UUID"),\r
X_Object_Hash = response.Headers.GetFirstValue("X-Object-Hash"),\r
Content_Type = response.Headers.GetFirstValue("Content-Type"),\r
ContentEncoding =\r
response.Content.Headers.ContentEncoding.FirstOrDefault(),\r
ContendDisposition =\r
- response.Content.Headers.ContentDisposition.ToString(),\r
+ response.Content.Headers.ContentDisposition.NullSafe(c=>c.ToString()),\r
Manifest = response.Headers.GetFirstValue("X-Object-Manifest"),\r
PublicUrl = response.Headers.GetFirstValue("X-Object-Public"),\r
StorageUri = StorageUrl,\r
});\r
\r
\r
- using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3, HttpCompletionOption.ResponseHeadersRead,\r
+ using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3, false,HttpCompletionOption.ResponseHeadersRead,\r
cancellationToken).ConfigureAwait(false))\r
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))\r
using(var targetStream=new MemoryStream())\r
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(@"application/octet-stream");\r
\r
//Send the block\r
- using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3,HttpCompletionOption.ResponseContentRead,token).ConfigureAwait(false))\r
+ using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3,false,HttpCompletionOption.ResponseContentRead,token).ConfigureAwait(false))\r
{ \r
Log.InfoFormat("[BLOCK POST PROGRESS] Completed ");\r
response.EnsureSuccessStatusCode();\r
}\r
}\r
\r
- public void DeleteObject(string account, Uri sourceContainer, Uri objectName, bool isDirectory)\r
+ public async Task DeleteObject(string account, Uri sourceContainer, Uri objectName, bool isDirectory)\r
{\r
if (sourceContainer == null)\r
throw new ArgumentNullException("sourceContainer", "The sourceContainer property can't be empty");\r
throw new ArgumentException("The objectName must be relative","objectName");\r
Contract.EndContractBlock();\r
\r
+\r
+\r
+ var sourceUri = new Uri(String.Format("/{0}/{1}", sourceContainer, objectName),UriKind.Relative);\r
+\r
+ \r
+ if (objectName.OriginalString.EndsWith(".ignore"))\r
+ using(var response = await _baseHttpClient.DeleteAsync(sourceUri)){}\r
+ else\r
+ {\r
+ var relativeUri = new Uri(String.Format("{0}/{1}", FolderConstants.TrashContainer, objectName),\r
+ UriKind.Relative);\r
+\r
+/*\r
+ var relativeUri = isDirectory\r
+ ? new Uri(\r
+ String.Format("{0}/{1}?delimiter=/", FolderConstants.TrashContainer,\r
+ objectName), UriKind.Relative)\r
+ : new Uri(String.Format("{0}/{1}", FolderConstants.TrashContainer, objectName),\r
+ UriKind.Relative);\r
+\r
+*/\r
+ var targetUri = GetTargetUri(account).Combine(relativeUri);\r
+\r
+\r
+ var message = new HttpRequestMessage(HttpMethod.Put, targetUri);\r
+ message.Headers.Add("X-Move-From", sourceUri.ToString());\r
+\r
+ Log.InfoFormat("[TRASH] [{0}] to [{1}]", sourceUri, targetUri);\r
+ using (var response = await _baseHttpClient.SendAsyncWithRetries(message, 3))\r
+ {\r
+ var expectedCodes = new[]\r
+ {\r
+ HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created,\r
+ HttpStatusCode.NotFound\r
+ };\r
+ if (!expectedCodes.Contains(response.StatusCode))\r
+ throw CreateWebException("DeleteObject", response.StatusCode);\r
+ }\r
+ }\r
+/*\r
+ \r
+\r
var targetUrl = FolderConstants.TrashContainer + "/" + objectName;\r
/*\r
if (isDirectory)\r
targetUrl = targetUrl + "?delimiter=/";\r
-*/\r
+#1#\r
\r
var sourceUrl = String.Format("/{0}/{1}", sourceContainer, objectName);\r
\r
if (!expectedCodes.Contains(client.StatusCode))\r
throw CreateWebException("DeleteObject", client.StatusCode);\r
}\r
+*/\r
}\r
\r
\r
}\r
\r
\r
- public bool CanUpload(string account, ObjectInfo cloudFile)\r
+ public async Task<bool> CanUpload(string account, ObjectInfo cloudFile)\r
{\r
Contract.Requires(!String.IsNullOrWhiteSpace(account));\r
Contract.Requires(cloudFile!=null);\r
\r
- using (var client = new RestClient(_baseClient))\r
- {\r
- if (!String.IsNullOrWhiteSpace(account))\r
- client.BaseAddress = GetAccountUrl(account);\r
-\r
-\r
var parts = cloudFile.Name.ToString().Split('/');\r
var folder = String.Join("/", parts,0,parts.Length-1);\r
\r
var fileName = String.Format("{0}/{1}.pithos.ignore", folder, Guid.NewGuid());\r
var fileUri=fileName.ToEscapedUri(); \r
\r
- client.Parameters.Clear();\r
try\r
{\r
var relativeUri = cloudFile.Container.Combine(fileUri);\r
- client.PutWithRetry(relativeUri, 3, @"application/octet-stream");\r
-\r
+ var targetUri = GetTargetUri(account).Combine(relativeUri);\r
+ var message = new HttpRequestMessage(HttpMethod.Put, targetUri);\r
+ message.Content.Headers.ContentType =new MediaTypeHeaderValue("application/octet-stream");\r
+ var response=await _baseHttpClient.SendAsyncWithRetries(message, 3); \r
var expectedCodes = new[] { HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created};\r
- var result=(expectedCodes.Contains(client.StatusCode));\r
- DeleteObject(account, cloudFile.Container, fileUri, cloudFile.IsDirectory);\r
+ var result=(expectedCodes.Contains(response.StatusCode));\r
+ await DeleteObject(account, cloudFile.Container, fileUri, cloudFile.IsDirectory);\r
return result;\r
}\r
catch\r
{\r
return false;\r
}\r
- }\r
+ \r
}\r
\r
~CloudFilesClient()\r
#region Object operations \r
Task GetObject(string account, Uri container, Uri objectName, string fileName, CancellationToken cancellationToken);\r
Task PutObject(string account, Uri container, Uri objectName, string fileName, string hash = null, string contentType = "application/octet-stream");\r
- void DeleteObject(string account, Uri container, Uri objectName, bool isDirectory);\r
+ Task DeleteObject(string account, Uri container, Uri objectName, bool isDirectory);\r
//void DeleteObject(string container, string objectName, string account = null);\r
void MoveObject(string account, Uri sourceContainer, Uri oldObjectName, Uri targetContainer, Uri newObjectName);\r
bool ObjectExists(string account, Uri container, Uri objectName);\r
return default(Task);\r
}\r
\r
- public void DeleteObject(string account, Uri container, Uri objectName, bool isDirectory)\r
+ public Task DeleteObject(string account, Uri container, Uri objectName, bool isDirectory)\r
{\r
\r
Contract.Requires(!String.IsNullOrWhiteSpace(Token));\r
Contract.Requires(!container.IsAbsoluteUri);\r
Contract.Requires(objectName != null);\r
Contract.Requires(!objectName.IsAbsoluteUri);\r
+\r
+ return default(Task);\r
}\r
\r
public void MoveObject(string account, Uri sourceContainer, Uri oldObjectName, Uri targetContainer, Uri newObjectName)\r
<HintPath>..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll</HintPath>\r
</Reference>\r
<Reference Include="System.ServiceModel" />\r
+ <Reference Include="System.ServiceModel.Channels" />\r
<Reference Include="System.Threading.Tasks.Dataflow">\r
<HintPath>..\Libraries\System.Threading.Tasks.Dataflow.dll</HintPath>\r
</Reference>\r
using System.Threading;\r
using System.Threading.Tasks;\r
using log4net;\r
+using System.ServiceModel.Channels;\r
\r
namespace Pithos.Network\r
{\r
\r
}\r
\r
- public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries)\r
+ public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries,bool acceptNotFound=false)\r
{\r
- return client.HeadAsyncWithRetries(requestUri, retries, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
+ return client.HeadAsyncWithRetries(requestUri, retries, acceptNotFound,HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
}\r
\r
- public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
+ public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, bool acceptNotFound,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
{\r
- return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Head, requestUri), retries, completionOption, cancellationToken);\r
+ return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Head, requestUri), retries, acceptNotFound,completionOption, cancellationToken);\r
}\r
\r
public static Task<HttpResponseMessage> GetAsyncWithRetries(this HttpClient client, Uri requestUri, int retries)\r
\r
public static Task<HttpResponseMessage> GetAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
{\r
- return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Get, requestUri), retries, completionOption, cancellationToken);\r
+ return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Get, requestUri), retries, false,completionOption, cancellationToken);\r
}\r
\r
\r
public static Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries)\r
{\r
- return client.SendAsyncWithRetries(message, retries, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
+ return client.SendAsyncWithRetries(message, retries,false, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
}\r
\r
- public static async Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
+ public static async Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries,bool acceptNotFound,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
{\r
var waitTime = TimeSpan.FromSeconds(10);\r
- var acceptedCodes = new[] { HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
+ var acceptedCodes =acceptNotFound\r
+ ? new[] {HttpStatusCode.NotFound, HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
+ HttpStatusCode.RedirectMethod,HttpStatusCode.NotModified,HttpStatusCode.TemporaryRedirect,HttpStatusCode.RedirectKeepVerb,HttpStatusCode.Conflict}\r
+ : new[] { HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
HttpStatusCode.RedirectMethod,HttpStatusCode.NotModified,HttpStatusCode.TemporaryRedirect,HttpStatusCode.RedirectKeepVerb,HttpStatusCode.Conflict};\r
+ \r
+ \r
while (retries > 0)\r
{\r
var timedOut = false;\r
if (Log.IsDebugEnabled)\r
Log.DebugFormat("[REQUEST] {0}", message);\r
HttpResponseMessage result=null;\r
+ \r
+ \r
+ \r
try\r
{\r
result = await client.SendAsync(message, completionOption, cancellationToken).ConfigureAwait(false);\r
if (Log.IsDebugEnabled)\r
Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL WITH TIMEOUT", message.Method, message.RequestUri);\r
}\r
+ catch(TaskCanceledException exc)\r
+ {\r
+ //If the task was cancelled due to a timeout, retry it\r
+ if (!exc.CancellationToken.IsCancellationRequested)\r
+ {\r
+ timedOut = true;\r
+ if (Log.IsDebugEnabled)\r
+ Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL WITH TIMEOUT", message.Method, message.RequestUri);\r
+ }\r
+ else\r
+ {\r
+ throw;\r
+ }\r
+ }\r
catch (Exception exc)\r
{\r
Log.FatalFormat("Unexpected error while sending:\n{0}\n{1}", message, exc);\r