Added notification messages for conflicts and multifile operations on Pithos.Core...
[pithos-ms-client] / trunk / Pithos.Core / Agents / NetworkAgent.cs
index 8f46ded..6ce712a 100644 (file)
@@ -76,8 +76,12 @@ namespace Pithos.Core.Agents
 
         private readonly ConcurrentBag<AccountInfo> _accounts = new ConcurrentBag<AccountInfo>();
 
+        [System.ComponentModel.Composition.Import]
+        public IPithosSettings Settings { get; set; }
 
         private bool _firstPoll = true;
+        private TaskCompletionSource<bool> _tcs;
+
         public void Start()
         {
             _firstPoll = true;
@@ -428,12 +432,28 @@ namespace Pithos.Core.Agents
             }
         }*/
 
-        
+        public void SynchNow()
+        {             
+            if (_tcs!=null)
+                _tcs.SetResult(true);
+            else
+            {
+                //TODO: This may be OK for testing purposes, but we have no guarantee that it will
+                //work properly in production
+                PollRemoteFiles(repeat:false);
+            }
+        }
 
         //Remote files are polled periodically. Any changes are processed
-        public async Task ProcessRemoteFiles(DateTime? since = null)
-        {            
-            await TaskEx.Delay(TimeSpan.FromSeconds(10),_agent.CancellationToken);
+        public async Task PollRemoteFiles(DateTime? since = null,bool repeat=true)
+        {
+
+            _tcs = new TaskCompletionSource<bool>();
+            var wait = TaskEx.Delay(TimeSpan.FromSeconds(Settings.PollingInterval), _agent.CancellationToken);
+            var signaledTask=await TaskEx.WhenAny(_tcs.Task,wait);
+            //If polling is signalled by SynchNow, ignore the since tag
+            if (signaledTask is Task<bool>)
+                since = null;
 
             using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push("All accounts"))
             {
@@ -450,13 +470,14 @@ namespace Pithos.Core.Agents
                     await TaskEx.WhenAll(tasks.ToList());
 
                     _firstPoll = false;
-                    ProcessRemoteFiles(nextSince);
+                    if (repeat)
+                        PollRemoteFiles(nextSince);
                 }
                 catch (Exception ex)
                 {
                     Log.ErrorFormat("Error while processing accounts\r\n{0}",ex);
                     //In case of failure retry with the same parameter
-                    ProcessRemoteFiles(since);
+                    PollRemoteFiles(since);
                 }
                 
 
@@ -547,22 +568,22 @@ namespace Pithos.Core.Agents
                         ProcessTrashedFiles(accountInfo, realTrash);
 
 
-                        var cleanRemotes = from info in remoteObjects
+                        var cleanRemotes = (from info in remoteObjects
                                      //.Union(sharedObjects)
                                      let name = info.Name
                                      where !name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase) &&
                                            !name.StartsWith(FolderConstants.CacheFolder + "/",
                                                             StringComparison.InvariantCultureIgnoreCase)
-                                     select info;
+                                     select info).ToList();
 
 
-                        
 
+                        ProcessDeletedFiles(accountInfo, cleanRemotes, pollTime);
 
                         //Create a list of actions from the remote files
                         var allActions = ObjectsToActions(accountInfo, cleanRemotes);
 
-                        ProcessDeletedFiles(accountInfo, cleanRemotes, pollTime);
+                        
                         //var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
 
                         //And remove those that are already being processed by the agent
@@ -606,20 +627,44 @@ namespace Pithos.Core.Agents
                 throw new ArgumentNullException("cloudFiles");
             Contract.EndContractBlock();
 
-            if (_firstPoll) return;
-
-            var deleteCandidates = from state in FileState.Queryable
-                                   let stateUrl = FileInfoExtensions.FromPath(state.FilePath)
-                                       .AsRelativeUrlTo(accountInfo.AccountPath)
-                                   where state.Modified <= pollTime &&
-                                         !cloudFiles.Any(r => r.Name == stateUrl)
-                                   select state;
-
-            foreach (var deleteCandidate in deleteCandidates)
+            //Check the Modified date to ensure that were just created and haven't been uploaded yet
+            //NOTE: The NHibernate LINQ provider doesn't support custom functions so we need to break the query 
+            //in two steps
+            //NOTE: DON'T return files that are already in conflict. The first poll would mark them as 
+            //"In Conflict" but subsequent polls would delete them
+            var deleteCandidates = (from state in FileState.Queryable
+                                   where 
+                                        state.Modified <= pollTime 
+                                        && state.FilePath.StartsWith(accountInfo.AccountPath)
+                                        && state.FileStatus != FileStatus.Conflict
+                                   select state).ToList();
+
+            var filesToDelete = (from deleteCandidate in deleteCandidates 
+                         let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath) 
+                         let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath) 
+                         where !cloudFiles.Any(r => Path.Combine(r.Container, r.Name) == relativeFilePath) 
+                         select localFile).ToList();
+
+            //On the first run
+            if (_firstPoll)
+            {
+                //Set the status of missing files to Conflict
+                foreach (var item in filesToDelete)
+                {
+                    StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted);
+                }
+                StatusNotification.NotifyConflicts(filesToDelete, String.Format("{0} local files are missing from Pithos, possibly because they were deleted",filesToDelete.Count));
+            }
+            else
             {
-                File.Delete(deleteCandidate.FilePath);
-                StatusKeeper.ClearFileStatus(deleteCandidate.FilePath);
+                foreach (var item in filesToDelete)
+                {
+                    item.Delete();
+                    StatusKeeper.ClearFileStatus(item.FullName);
+                }
+                StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted",filesToDelete.Count),TraceLevel.Info);
             }
+
         }
 
         private static void CreateContainerFolders(AccountInfo accountInfo, IEnumerable<ContainerInfo> containers)