-using System;
+#region
+/* -----------------------------------------------------------------------
+ * <copyright file="FileAgent.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;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
namespace Pithos.Core.Agents
{
- [Export]
+// [Export]
public class FileAgent
{
Agent<WorkflowState> _agent;
private FileSystemWatcher _watcher;
+ private FileSystemWatcherAdapter _adapter;
- [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; }
AccountInfo = accountInfo;
RootPath = rootPath;
- _watcher = new FileSystemWatcher(rootPath);
- _watcher.IncludeSubdirectories = true;
- _watcher.Changed += OnFileEvent;
- _watcher.Created += OnFileEvent;
- _watcher.Deleted += OnFileEvent;
- _watcher.Renamed += OnRenameEvent;
+ _watcher = new FileSystemWatcher(rootPath) {IncludeSubdirectories = true};
+ _adapter = new FileSystemWatcherAdapter(_watcher);
+
+ _adapter.Changed += OnFileEvent;
+ _adapter.Created += OnFileEvent;
+ _adapter.Deleted += OnFileEvent;
+ _adapter.Renamed += OnRenameEvent;
+ _adapter.Moved += OnMoveEvent;
_watcher.EnableRaisingEvents = true;
loop = () =>
{
var message = inbox.Receive();
- var process=message.Then(Process,inbox.CancellationToken);
-
+ var process=message.Then(Process,inbox.CancellationToken);
inbox.LoopAsync(process,loop,ex=>
Log.ErrorFormat("[ERROR] File Event Processing:\r{0}", ex));
};
throw new ArgumentNullException("state");
Contract.EndContractBlock();
- Debug.Assert(!Ignore(state.Path));
+ if (Ignore(state.Path))
+ return CompletedTask<object>.Default;
var networkState = NetworkGate.GetNetworkState(state.Path);
//Skip if the file is already being downloaded or uploaded and
}
catch (Exception exc)
{
- Log.WarnFormat("Error occured while indexing{0. The file will be skipped}\n{1}", state.Path, exc);
+ Log.WarnFormat("Error occured while indexing{0}. The file will be skipped\n{1}",
+ state.Path, exc);
}
return CompletedTask<object>.Default;
}
-
-/*
- private Task Process(Task<WorkflowState> action)
- {
- return action.ContinueWith(t => Process(t.Result));
- }
-*/
-
-
public bool Pause
{
get { return _watcher == null || !_watcher.EnableRaisingEvents; }
return monitoredFiles;
}
+ public IEnumerable<string> EnumerateFilesSystemInfosAsRelativeUrls(string searchPattern="*")
+ {
+ var rootDir = new DirectoryInfo(RootPath);
+ var monitoredFiles = from file in rootDir.EnumerateFileSystemInfos(searchPattern, SearchOption.AllDirectories)
+ where !Ignore(file.FullName)
+ select file.AsRelativeUrlTo(RootPath);
+ return monitoredFiles;
+ }
+
private bool Ignore(string filePath)
{
+ //Ignore all first-level directories and files (ie at the container folders level)
+ if (FoundBelowRoot(filePath, RootPath,1))
+ return true;
+
+ //Ignore first-level items under the "others" folder (ie at the accounts folders level).
+ var othersPath = Path.Combine(RootPath, FolderConstants.OthersFolder);
+ if (FoundBelowRoot(filePath, othersPath,1))
+ return true;
+
+ //Ignore second-level (container) folders under the "others" folder (ie at the container folders level).
+ if (FoundBelowRoot(filePath, othersPath,2))
+ return true;
+
+
+ //Ignore anything happening in the cache path
if (filePath.StartsWith(CachePath))
return true;
if (_ignoreFiles.ContainsKey(filePath.ToLower()))
return true;
- return false;
+
+ //Ignore if selective synchronization is defined,
+ return SelectivePaths.Count > 0
+ //And the target file is not below any of the selective paths
+ && !SelectivePaths.Any(filePath.IsAtOrDirectlyBelow);
+ }
+
+/* private static bool FoundInRoot(string filePath, string rootPath)
+ {
+ //var rootDirectory = new DirectoryInfo(rootPath);
+
+ //If the paths are equal, return true
+ if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))
+ return true;
+
+ //If the filepath is below the root path
+ if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))
+ {
+ //Get the relative path
+ var relativePath = filePath.Substring(rootPath.Length + 1);
+ //If the relativePath does NOT contains a path separator, we found a match
+ return (!relativePath.Contains(@"\"));
+ }
+
+ //If the filepath is not under the root path, return false
+ return false;
+ }*/
+
+
+ private static bool FoundBelowRoot(string filePath, string rootPath,int level)
+ {
+ //var rootDirectory = new DirectoryInfo(rootPath);
+
+ //If the paths are equal, return true
+ if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))
+ return true;
+
+ //If the filepath is below the root path
+ if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))
+ {
+ //Get the relative path
+ var relativePath = filePath.Substring(rootPath.Length + 1);
+ //If the relativePath does NOT contains a path separator, we found a match
+ var levels=relativePath.ToCharArray().Count(c=>c=='\\')+1;
+ return levels==level;
+ }
+
+ //If the filepath is not under the root path, return false
+ return false;
}
//Post a Change message for all events except rename
//Ignore events that affect the cache folder
var filePath = e.FullPath;
if (Ignore(filePath))
- return;
-
+ return;
+
_agent.Post(new WorkflowState{AccountInfo=AccountInfo, Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
}
});
}
+ //Post a Change message for renames containing the old and new names
+ void OnMoveEvent(object sender, MovedEventArgs e)
+ {
+ var oldFullPath = e.OldFullPath;
+ var fullPath = e.FullPath;
+ if (Ignore(oldFullPath) || Ignore(fullPath))
+ return;
+
+ _agent.Post(new WorkflowState
+ {
+ AccountInfo=AccountInfo,
+ OldPath = oldFullPath,
+ OldFileName = e.OldName,
+ Path = fullPath,
+ FileName = e.Name,
+ TriggeringChange = e.ChangeType
+ });
+ }
+
private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
- {
+ {
{WatcherChangeTypes.Created,FileStatus.Created},
{WatcherChangeTypes.Changed,FileStatus.Modified},
{WatcherChangeTypes.Deleted,FileStatus.Deleted},
{WatcherChangeTypes.Renamed,FileStatus.Renamed}
};
- private Dictionary<string,string> _ignoreFiles=new Dictionary<string, string>();
+ private Dictionary<string, string> _ignoreFiles=new Dictionary<string, string>();
private WorkflowState UpdateFileStatus(WorkflowState state)
{
return false;
}
- public FileInfo GetFileInfo(string relativePath)
+ public static FileAgent GetFileAgent(AccountInfo accountInfo)
+ {
+ return GetFileAgent(accountInfo.AccountPath);
+ }
+
+ public static FileAgent GetFileAgent(string rootPath)
+ {
+ return AgentLocator<FileAgent>.Get(rootPath.ToLower());
+ }
+
+
+ public FileSystemInfo GetFileSystemInfo(string relativePath)
{
if (String.IsNullOrWhiteSpace(relativePath))
throw new ArgumentNullException("relativePath");
Contract.EndContractBlock();
var absolutePath = Path.Combine(RootPath, relativePath);
-// Debug.Assert(File.Exists(absolutePath),String.Format("Path {0} doesn't exist",absolutePath));
- return new FileInfo(absolutePath);
+ if (Directory.Exists(absolutePath))
+ return new DirectoryInfo(absolutePath).WithProperCapitalization();
+ else
+ return new FileInfo(absolutePath).WithProperCapitalization();
}
public void Delete(string relativePath)
{
- var absolutePath = Path.Combine(RootPath, relativePath);
+ var absolutePath = Path.Combine(RootPath, relativePath).ToLower();
if (File.Exists(absolutePath))
- {
- File.Delete(absolutePath);
- _ignoreFiles[absolutePath.ToLower()] = absolutePath.ToLower();
+ {
+ try
+ {
+ File.Delete(absolutePath);
+ }
+ //The file may have been deleted by another thread. Just ignore the relevant exception
+ catch (FileNotFoundException) { }
}
+ else if (Directory.Exists(absolutePath))
+ {
+ try
+ {
+ Directory.Delete(absolutePath, true);
+ }
+ //The directory may have been deleted by another thread. Just ignore the relevant exception
+ catch (DirectoryNotFoundException){}
+ }
+
+ //_ignoreFiles[absolutePath] = absolutePath;
StatusKeeper.ClearFileStatus(absolutePath);
}
}