using Pithos.Interfaces;
using System.ServiceModel;
using Pithos.Network;
+using log4net;
namespace Pithos.Core
{
[Export(typeof(PithosMonitor))]
public class PithosMonitor:IDisposable
{
- private const string PithosContainer = "pithos";
- private const string TrashContainer = "trash";
-
- private const string FragmentsFolder = "fragments";
-
private int _blockSize;
private string _blockHash;
[Import]
public IPithosWorkflow Workflow { get; set; }
- [Import]
public ICloudClient CloudClient { get; set; }
- [Import]
- public ICloudClient CloudListeningClient { get; set; }
-
public IStatusNotification StatusNotification { get; set; }
[Import]
public WorkflowAgent WorkflowAgent { get; set; }
[Import]
- public NetworkAgent NetworkAgent { get; set; }
-
+ public NetworkAgent NetworkAgent { get; set; }
public string UserName { get; set; }
public string ApiKey { get; set; }
- private ServiceHost _statusService { get; set; }
+ private AccountInfo _accountInfo;
+
+
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PithosMonitor));
public bool Pause
}
}
- public string RootPath { get; set; }
+ private string _rootPath;
+ public string RootPath
+ {
+ get { return _rootPath; }
+ set
+ {
+ _rootPath = String.IsNullOrWhiteSpace(value)
+ ? String.Empty
+ : value.ToLower();
+ }
+ }
CancellationTokenSource _cancellationSource;
- private bool _isInitialized;
+ private bool _started;
public void Start()
- {
+ {
+ if (String.IsNullOrWhiteSpace(ApiKey))
+ throw new InvalidOperationException("The ApiKey is empty");
+ if (String.IsNullOrWhiteSpace(UserName))
+ throw new InvalidOperationException("The UserName is empty");
+ if (String.IsNullOrWhiteSpace(AuthenticationUrl))
+ throw new InvalidOperationException("The Authentication url is empty");
+ Contract.EndContractBlock();
+
StatusNotification.NotifyChange("Starting");
- if (_isInitialized)
+ if (_started)
{
if (!_cancellationSource.IsCancellationRequested)
return;
}
_cancellationSource = new CancellationTokenSource();
+ _started = true;
+ CloudClient=new CloudFilesClient(UserName,ApiKey);
var proxyUri = ProxyFromSettings();
CloudClient.Proxy = proxyUri;
CloudClient.UsePithos = this.UsePithos;
-
+ CloudClient.AuthenticationUrl = this.AuthenticationUrl;
+
+ _accountInfo = CloudClient.Authenticate();
+ _accountInfo.AccountPath = RootPath;
+
+
+ var pithosFolder = Path.Combine(RootPath, FolderConstants.PithosContainer);
+ if (!Directory.Exists(pithosFolder))
+ Directory.CreateDirectory(pithosFolder);
+ //Create the cache folder and ensure it is hidden
+ CreateHiddenFolder(RootPath, FolderConstants.CacheFolder);
+
+ var policy=CloudClient.GetAccountPolicies(_accountInfo);
+
+ StatusNotification.NotifyAccount(policy);
EnsurePithosContainers();
StatusKeeper.BlockHash = _blockHash;
StatusKeeper.BlockSize = _blockSize;
StatusKeeper.StartProcessing(_cancellationSource.Token);
- IndexLocalFiles(RootPath);
- StartWatcherAgent(RootPath);
- StartStatusService();
+ IndexLocalFiles();
+ StartWatcherAgent();
+
+ StartNetworkAgent();
+
StartWorkflowAgent();
- WorkflowAgent.RestartInterruptedFiles();
- _isInitialized = true;
+ WorkflowAgent.RestartInterruptedFiles(_accountInfo);
}
private void EnsurePithosContainers()
{
- CloudClient.UsePithos = this.UsePithos;
- CloudClient.AuthenticationUrl = this.AuthenticationUrl;
- CloudClient.Authenticate(UserName, ApiKey);
- var pithosContainers = new[] { TrashContainer,PithosContainer};
+ //Create the two default containers if they are missing
+ var pithosContainers = new List<string>{ FolderConstants.TrashContainer,FolderConstants.PithosContainer};
foreach (var container in pithosContainers)
- {
- var info=CloudClient.GetContainerInfo(container);
+ {
+ var info=CloudClient.GetContainerInfo(this.UserName, container);
if (info == ContainerInfo.Empty)
{
- CloudClient.CreateContainer(container);
- info = CloudClient.GetContainerInfo(container);
+ CloudClient.CreateContainer(this.UserName, container);
+ info = CloudClient.GetContainerInfo(this.UserName, container);
}
_blockSize = info.BlockSize;
_blockHash = info.BlockHash;
+ _accountInfo.BlockSize = _blockSize;
+ _accountInfo.BlockHash = _blockHash;
}
-
- var allContainers= CloudClient.ListContainers();
- var extraContainers = from container in allContainers
- where !pithosContainers.Contains(container.Name.ToLower())
- select container;
-
-
-
}
public string AuthenticationUrl { get; set; }
return null;
}
- private void IndexLocalFiles(string path)
+ private void IndexLocalFiles()
{
StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info);
- Trace.TraceInformation("[START] Index Local");
- try
- {
- var fragmentsPath=Path.Combine(RootPath, FragmentsFolder);
- var files =
- from filePath in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).AsParallel()
- where !filePath.StartsWith(fragmentsPath,StringComparison.InvariantCultureIgnoreCase) &&
- !filePath.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase)
- select filePath.ToLower();
- StatusKeeper.StoreUnversionedFiles(files);
-
- }
- catch (Exception exc)
- {
- Trace.TraceError("[ERROR] Index Local - {0}", exc);
- }
- finally
+ using (log4net.ThreadContext.Stacks["Monitor"].Push("Indexing local files"))
{
- Trace.TraceInformation("[END] Inxed Local");
+ Log.Info("START");
+ try
+ {
+ var cachePath = Path.Combine(RootPath, FolderConstants.CacheFolder);
+ var directory = new DirectoryInfo(RootPath);
+ var files =
+ from file in directory.EnumerateFiles("*", SearchOption.AllDirectories)
+ where !file.FullName.StartsWith(cachePath, StringComparison.InvariantCultureIgnoreCase) &&
+ !file.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase)
+ select file;
+ StatusKeeper.ProcessExistingFiles(files);
+
+ }
+ catch (Exception exc)
+ {
+ Log.Error("[ERROR]", exc);
+ }
+ finally
+ {
+ Log.Info("[END]");
+ }
}
}
- private void StartStatusService()
- {
- // Create a ServiceHost for the CalculatorService type and provide the base address.
- var baseAddress = new Uri("net.pipe://localhost/pithos");
- _statusService = new ServiceHost(typeof(StatusService), baseAddress);
-
- var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
-
- _statusService.AddServiceEndpoint(typeof(IStatusService), binding, "net.pipe://localhost/pithos/statuscache");
- _statusService.AddServiceEndpoint(typeof (ISettingsService), binding, "net.pipe://localhost/pithos/settings");
-
-
- //// Add a mex endpoint
- var smb = new ServiceMetadataBehavior
- {
- HttpGetEnabled = true,
- HttpGetUrl = new Uri("http://localhost:30000/pithos/mex")
- };
- _statusService.Description.Behaviors.Add(smb);
-
-
- _statusService.Open();
- }
-
- private void StopStatusService()
- {
- if (_statusService == null)
- return;
-
- if (_statusService.State == CommunicationState.Faulted)
- _statusService.Abort();
- else if (_statusService.State != CommunicationState.Closed)
- _statusService.Close();
- _statusService = null;
-
- }
+
private void StartWorkflowAgent()
try
{
- CloudClient.UsePithos = this.UsePithos;
- CloudClient.AuthenticationUrl = this.AuthenticationUrl;
- CloudClient.Authenticate(UserName, ApiKey);
-
- StartNetworkAgent(RootPath);
-
WorkflowAgent.StatusNotification = StatusNotification;
- WorkflowAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
WorkflowAgent.Start();
}
catch (Exception)
public bool UsePithos { get; set; }
-
internal class LocalFileComparer:EqualityComparer<CloudAction>
{
public override bool Equals(CloudAction x, CloudAction y)
return false;
if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
return false;
- if (x.CloudFile != null && y.CloudFile != null && !x.CloudFile.Hash.Equals(y.CloudFile.Hash))
- return false;
+ if (x.CloudFile != null && y.CloudFile != null )
+ {
+ if (x.CloudFile.Hash == null & y.CloudFile.Hash != null)
+ return false;
+ if (x.CloudFile.Hash != null & y.CloudFile.Hash == null)
+ return false;
+ if (x.CloudFile.Hash == null & y.CloudFile.Hash == null)
+ return (x.CloudFile.Name == y.CloudFile.Name);
+ if (!x.CloudFile.Hash.Equals(y.CloudFile.Hash))
+ return false;
+ }
if (x.CloudFile == null ^ y.CloudFile == null ||
x.LocalFile == null ^ y.LocalFile == null)
return false;
public override int GetHashCode(CloudAction obj)
{
var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
- var hash2 = (obj.CloudFile == null) ? int.MaxValue : obj.CloudFile.Hash.GetHashCode();
+ var hash2 = (obj.CloudFile == null) ? int.MaxValue : (obj.CloudFile.Hash ?? obj.CloudFile.Name??"").GetHashCode();
var hash3 = obj.Action.GetHashCode();
return hash1 ^ hash2 & hash3;
}
private Timer timer;
- private void StartNetworkAgent(string accountPath)
+ private void StartNetworkAgent()
{
- NetworkAgent.StatusNotification = StatusNotification;
- NetworkAgent.Start(PithosContainer, TrashContainer,_blockSize,_blockHash);
+ NetworkAgent.AddAccount(_accountInfo);
- NetworkAgent.ProcessRemoteFiles(accountPath);
+ NetworkAgent.StatusNotification = StatusNotification;
+
+ NetworkAgent.Start();
+
+ NetworkAgent.ProcessRemoteFiles();
}
- //Make sure a hidden fragments folder exists to store partial downloads
+ //Make sure a hidden cache folder exists to store partial downloads
private static string CreateHiddenFolder(string rootPath, string folderName)
{
if (String.IsNullOrWhiteSpace(rootPath))
var info = Directory.CreateDirectory(folder);
info.Attributes |= FileAttributes.Hidden;
- Trace.TraceInformation("Created Fragments Folder: {0}", folder);
+ Log.InfoFormat("Created cache Folder: {0}", folder);
+ }
+ else
+ {
+ var info = new DirectoryInfo(folder);
+ if ((info.Attributes & FileAttributes.Hidden) == 0)
+ {
+ info.Attributes |= FileAttributes.Hidden;
+ Log.InfoFormat("Reset cache folder to hidden: {0}", folder);
+ }
}
return folder;
}
- private void StartWatcherAgent(string path)
+ private void StartWatcherAgent()
{
+ AgentLocator<FileAgent>.Register(FileAgent,RootPath);
+
FileAgent.StatusKeeper = StatusKeeper;
FileAgent.Workflow = Workflow;
- FileAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
- FileAgent.Start(path);
+ FileAgent.CachePath = Path.Combine(RootPath, FolderConstants.CacheFolder);
+ FileAgent.Start(_accountInfo, RootPath);
}
public void Stop()
- {
- FileAgent.Stop();
+ {
+ AgentLocator<FileAgent>.Remove(RootPath);
+
+ if (FileAgent!=null)
+ FileAgent.Stop();
+ FileAgent = null;
if (timer != null)
timer.Dispose();
- timer = null;
- StopStatusService();
+ timer = null;
}
}
+ public void MoveFileStates(string oldPath, string newPath)
+ {
+ if (String.IsNullOrWhiteSpace(oldPath))
+ throw new ArgumentNullException("oldPath");
+ if (!Path.IsPathRooted(oldPath))
+ throw new ArgumentException("oldPath must be an absolute path","oldPath");
+ if (string.IsNullOrWhiteSpace(newPath))
+ throw new ArgumentNullException("newPath");
+ if (!Path.IsPathRooted(newPath))
+ throw new ArgumentException("newPath must be an absolute path","newPath");
+ Contract.EndContractBlock();
+
+ StatusKeeper.ChangeRoots(oldPath, newPath);
+ }
+
+ public void AddSelectivePaths(string[] added)
+ {
+ /* FileAgent.SelectivePaths.AddRange(added);
+ NetworkAgent.SyncPaths(added);*/
+ }
+
+ public void RemoveSelectivePaths(string[] removed)
+ {
+ FileAgent.SelectivePaths.RemoveAll(removed.Contains);
+ foreach (var removedPath in removed.Where(Directory.Exists))
+ {
+ Directory.Delete(removedPath,true);
+ }
+ }
+
+ public IEnumerable<string> GetRootFolders()
+ {
+ var dirs = from container in CloudClient.ListContainers(UserName)
+ from dir in CloudClient.ListObjects(UserName, container.Name, "")
+ select dir.Name;
+ return dirs;
+ }
+
+ public ObjectInfo GetObjectInfo(string filePath)
+ {
+ if (String.IsNullOrWhiteSpace(filePath))
+ throw new ArgumentNullException("filePath");
+ Contract.EndContractBlock();
+
+ var file=new FileInfo(filePath);
+ string relativeUrl;//=file.AsRelativeUrlTo(this.RootPath);
+ var relativePath = file.AsRelativeTo(RootPath);
+
+ string accountName,container;
+
+ var parts=relativePath.Split('\\');
+
+ var accountInfo = _accountInfo;
+ if (relativePath.StartsWith(FolderConstants.OthersFolder))
+ {
+ accountName = parts[1];
+ container = parts[2];
+ relativeUrl = String.Join("/", parts.Splice(3));
+ //Create the root URL for the target account
+ var oldName = UserName;
+ var absoluteUri = _accountInfo.StorageUri.AbsoluteUri;
+ var nameIndex=absoluteUri.IndexOf(oldName);
+ var root=absoluteUri.Substring(0, nameIndex);
+
+ accountInfo = new AccountInfo
+ {
+ UserName = accountName,
+ AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
+ StorageUri = new Uri(root + accountName),
+ BlockHash=accountInfo.BlockHash,
+ BlockSize=accountInfo.BlockSize,
+ Token=accountInfo.Token
+ };
+ }
+ else
+ {
+ accountName = this.UserName;
+ container = parts[0];
+ relativeUrl = String.Join("/", parts.Splice(1));
+ }
+
+ var client = new CloudFilesClient(accountInfo);
+ var objectInfo=client.GetObjectInfo(accountName, container, relativeUrl);
+ return objectInfo;
+ }
+
+ public ContainerInfo GetContainerInfo(string filePath)
+ {
+ if (String.IsNullOrWhiteSpace(filePath))
+ throw new ArgumentNullException("filePath");
+ Contract.EndContractBlock();
+
+ var file=new FileInfo(filePath);
+ var relativePath = file.AsRelativeTo(RootPath);
+
+ string accountName,container;
+
+ var parts=relativePath.Split('\\');
+
+ var accountInfo = _accountInfo;
+ if (relativePath.StartsWith(FolderConstants.OthersFolder))
+ {
+ accountName = parts[1];
+ container = parts[2];
+ //Create the root URL for the target account
+ var oldName = UserName;
+ var absoluteUri = _accountInfo.StorageUri.AbsoluteUri;
+ var nameIndex=absoluteUri.IndexOf(oldName);
+ var root=absoluteUri.Substring(0, nameIndex);
+
+ accountInfo = new AccountInfo
+ {
+ UserName = accountName,
+ AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
+ StorageUri = new Uri(root + accountName),
+ BlockHash=accountInfo.BlockHash,
+ BlockSize=accountInfo.BlockSize,
+ Token=accountInfo.Token
+ };
+ }
+ else
+ {
+ accountName = UserName;
+ container = parts[0];
+ }
+
+ var client = new CloudFilesClient(accountInfo);
+ var containerInfo=client.GetContainerInfo(accountName, container);
+ return containerInfo;
+ }
}
+
public interface IStatusNotification
{
void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
void NotifyChangedFile(string filePath);
+ void NotifyAccount(AccountInfo policy);
}
}