Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / PollAgent.cs @ db8a9589

History | View | Annotate | Download (25.3 kB)

1 aa7ac00e Panagiotis Kanavos
#region
2 aa7ac00e Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 aa7ac00e Panagiotis Kanavos
 * <copyright file="PollAgent.cs" company="GRNet">
4 aa7ac00e Panagiotis Kanavos
 * 
5 aa7ac00e Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 aa7ac00e Panagiotis Kanavos
 *
7 aa7ac00e Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 aa7ac00e Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 aa7ac00e Panagiotis Kanavos
 * conditions are met:
10 aa7ac00e Panagiotis Kanavos
 *
11 aa7ac00e Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 aa7ac00e Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 aa7ac00e Panagiotis Kanavos
 *      disclaimer.
14 aa7ac00e Panagiotis Kanavos
 *
15 aa7ac00e Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 aa7ac00e Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 aa7ac00e Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 aa7ac00e Panagiotis Kanavos
 *      provided with the distribution.
19 aa7ac00e Panagiotis Kanavos
 *
20 aa7ac00e Panagiotis Kanavos
 *
21 aa7ac00e Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 aa7ac00e Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 aa7ac00e Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 aa7ac00e Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 aa7ac00e Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 aa7ac00e Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 aa7ac00e Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 aa7ac00e Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 aa7ac00e Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 aa7ac00e Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 aa7ac00e Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 aa7ac00e Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 aa7ac00e Panagiotis Kanavos
 *
34 aa7ac00e Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 aa7ac00e Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 aa7ac00e Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 aa7ac00e Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 aa7ac00e Panagiotis Kanavos
 * </copyright>
39 aa7ac00e Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 aa7ac00e Panagiotis Kanavos
 */
41 aa7ac00e Panagiotis Kanavos
#endregion
42 aa7ac00e Panagiotis Kanavos
43 aa7ac00e Panagiotis Kanavos
using System.Collections.Concurrent;
44 aa7ac00e Panagiotis Kanavos
using System.ComponentModel.Composition;
45 aa7ac00e Panagiotis Kanavos
using System.Diagnostics;
46 aa7ac00e Panagiotis Kanavos
using System.Diagnostics.Contracts;
47 aa7ac00e Panagiotis Kanavos
using System.IO;
48 db8a9589 Panagiotis Kanavos
using System.Reflection;
49 aa7ac00e Panagiotis Kanavos
using System.Threading;
50 aa7ac00e Panagiotis Kanavos
using System.Threading.Tasks;
51 aa7ac00e Panagiotis Kanavos
using Castle.ActiveRecord;
52 aa7ac00e Panagiotis Kanavos
using Pithos.Interfaces;
53 aa7ac00e Panagiotis Kanavos
using Pithos.Network;
54 aa7ac00e Panagiotis Kanavos
using log4net;
55 aa7ac00e Panagiotis Kanavos
56 aa7ac00e Panagiotis Kanavos
namespace Pithos.Core.Agents
57 aa7ac00e Panagiotis Kanavos
{
58 aa7ac00e Panagiotis Kanavos
    using System;
59 aa7ac00e Panagiotis Kanavos
    using System.Collections.Generic;
60 aa7ac00e Panagiotis Kanavos
    using System.Linq;
61 aa7ac00e Panagiotis Kanavos
62 aa7ac00e Panagiotis Kanavos
    /// <summary>
63 aa7ac00e Panagiotis Kanavos
    /// PollAgent periodically polls the server to detect object changes. The agent retrieves a listing of all
64 aa7ac00e Panagiotis Kanavos
    /// objects and compares it with a previously cached version to detect differences. 
65 aa7ac00e Panagiotis Kanavos
    /// New files are downloaded, missing files are deleted from the local file system and common files are compared
66 aa7ac00e Panagiotis Kanavos
    /// to determine the appropriate action
67 aa7ac00e Panagiotis Kanavos
    /// </summary>
68 aa7ac00e Panagiotis Kanavos
    [Export]
69 aa7ac00e Panagiotis Kanavos
    public class PollAgent
70 aa7ac00e Panagiotis Kanavos
    {
71 db8a9589 Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
72 aa7ac00e Panagiotis Kanavos
73 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
74 aa7ac00e Panagiotis Kanavos
        public IStatusKeeper StatusKeeper { get; set; }
75 aa7ac00e Panagiotis Kanavos
76 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
77 aa7ac00e Panagiotis Kanavos
        public IPithosSettings Settings { get; set; }
78 aa7ac00e Panagiotis Kanavos
79 aa7ac00e Panagiotis Kanavos
        [System.ComponentModel.Composition.Import]
80 aa7ac00e Panagiotis Kanavos
        public NetworkAgent NetworkAgent { get; set; }
81 aa7ac00e Panagiotis Kanavos
82 aa7ac00e Panagiotis Kanavos
        public IStatusNotification StatusNotification { get; set; }
83 aa7ac00e Panagiotis Kanavos
84 aa7ac00e Panagiotis Kanavos
        private bool _firstPoll = true;
85 aa7ac00e Panagiotis Kanavos
86 aa7ac00e Panagiotis Kanavos
        //The Sync Event signals a manual synchronisation
87 aa7ac00e Panagiotis Kanavos
        private readonly AsyncManualResetEvent _syncEvent = new AsyncManualResetEvent();
88 aa7ac00e Panagiotis Kanavos
89 ec1a1baf Panagiotis Kanavos
        private readonly ConcurrentDictionary<string, DateTime> _lastSeen = new ConcurrentDictionary<string, DateTime>();
90 ec1a1baf Panagiotis Kanavos
        private readonly ConcurrentDictionary<string, AccountInfo> _accounts = new ConcurrentDictionary<string,AccountInfo>();
91 aa7ac00e Panagiotis Kanavos
92 aa7ac00e Panagiotis Kanavos
93 aa7ac00e Panagiotis Kanavos
        /// <summary>
94 aa7ac00e Panagiotis Kanavos
        /// Start a manual synchronization
95 aa7ac00e Panagiotis Kanavos
        /// </summary>
96 aa7ac00e Panagiotis Kanavos
        public void SynchNow()
97 aa7ac00e Panagiotis Kanavos
        {            
98 aa7ac00e Panagiotis Kanavos
            _syncEvent.Set();
99 aa7ac00e Panagiotis Kanavos
        }
100 aa7ac00e Panagiotis Kanavos
101 fec5da06 Panagiotis Kanavos
        /// <summary>
102 fec5da06 Panagiotis Kanavos
        /// Remote files are polled periodically. Any changes are processed
103 fec5da06 Panagiotis Kanavos
        /// </summary>
104 fec5da06 Panagiotis Kanavos
        /// <param name="since"></param>
105 fec5da06 Panagiotis Kanavos
        /// <returns></returns>
106 aa7ac00e Panagiotis Kanavos
        public async Task PollRemoteFiles(DateTime? since = null)
107 aa7ac00e Panagiotis Kanavos
        {
108 aa7ac00e Panagiotis Kanavos
            Debug.Assert(Thread.CurrentThread.IsBackground, "Polling Ended up in the main thread!");
109 aa7ac00e Panagiotis Kanavos
110 aa7ac00e Panagiotis Kanavos
            UpdateStatus(PithosStatus.Syncing);
111 aa7ac00e Panagiotis Kanavos
            StatusNotification.Notify(new PollNotification());
112 aa7ac00e Panagiotis Kanavos
113 ec1a1baf Panagiotis Kanavos
            using (ThreadContext.Stacks["Retrieve Remote"].Push("All accounts"))
114 aa7ac00e Panagiotis Kanavos
            {
115 aa7ac00e Panagiotis Kanavos
                //If this poll fails, we will retry with the same since value
116 aa7ac00e Panagiotis Kanavos
                var nextSince = since;
117 aa7ac00e Panagiotis Kanavos
                try
118 aa7ac00e Panagiotis Kanavos
                {
119 aa7ac00e Panagiotis Kanavos
                    //Next time we will check for all changes since the current check minus 1 second
120 aa7ac00e Panagiotis Kanavos
                    //This is done to ensure there are no discrepancies due to clock differences
121 aa7ac00e Panagiotis Kanavos
                    var current = DateTime.Now.AddSeconds(-1);
122 aa7ac00e Panagiotis Kanavos
123 ec1a1baf Panagiotis Kanavos
                    var tasks = from accountInfo in _accounts.Values
124 aa7ac00e Panagiotis Kanavos
                                select ProcessAccountFiles(accountInfo, since);
125 aa7ac00e Panagiotis Kanavos
126 aa7ac00e Panagiotis Kanavos
                    await TaskEx.WhenAll(tasks.ToList());
127 aa7ac00e Panagiotis Kanavos
128 aa7ac00e Panagiotis Kanavos
                    _firstPoll = false;
129 aa7ac00e Panagiotis Kanavos
                    //Reschedule the poll with the current timestamp as a "since" value
130 aa7ac00e Panagiotis Kanavos
                    nextSince = current;
131 aa7ac00e Panagiotis Kanavos
                }
132 aa7ac00e Panagiotis Kanavos
                catch (Exception ex)
133 aa7ac00e Panagiotis Kanavos
                {
134 aa7ac00e Panagiotis Kanavos
                    Log.ErrorFormat("Error while processing accounts\r\n{0}", ex);
135 aa7ac00e Panagiotis Kanavos
                    //In case of failure retry with the same "since" value
136 aa7ac00e Panagiotis Kanavos
                }
137 aa7ac00e Panagiotis Kanavos
138 aa7ac00e Panagiotis Kanavos
                UpdateStatus(PithosStatus.InSynch);
139 fec5da06 Panagiotis Kanavos
                //The multiple try blocks are required because we can't have an await call
140 fec5da06 Panagiotis Kanavos
                //inside a finally block
141 fec5da06 Panagiotis Kanavos
                //TODO: Find a more elegant solution for reschedulling in the event of an exception
142 fec5da06 Panagiotis Kanavos
                try
143 fec5da06 Panagiotis Kanavos
                {
144 fec5da06 Panagiotis Kanavos
                    //Wait for the polling interval to pass or the Sync event to be signalled
145 fec5da06 Panagiotis Kanavos
                    nextSince = await WaitForScheduledOrManualPoll(nextSince);
146 fec5da06 Panagiotis Kanavos
                }
147 fec5da06 Panagiotis Kanavos
                finally
148 fec5da06 Panagiotis Kanavos
                {
149 fec5da06 Panagiotis Kanavos
                    //Ensure polling is scheduled even in case of error
150 fec5da06 Panagiotis Kanavos
                    TaskEx.Run(() => PollRemoteFiles(nextSince));                        
151 fec5da06 Panagiotis Kanavos
                }
152 aa7ac00e Panagiotis Kanavos
            }
153 aa7ac00e Panagiotis Kanavos
        }
154 aa7ac00e Panagiotis Kanavos
155 aa7ac00e Panagiotis Kanavos
        /// <summary>
156 aa7ac00e Panagiotis Kanavos
        /// Wait for the polling period to expire or a manual sync request
157 aa7ac00e Panagiotis Kanavos
        /// </summary>
158 aa7ac00e Panagiotis Kanavos
        /// <param name="since"></param>
159 aa7ac00e Panagiotis Kanavos
        /// <returns></returns>
160 aa7ac00e Panagiotis Kanavos
        private async Task<DateTime?> WaitForScheduledOrManualPoll(DateTime? since)
161 aa7ac00e Panagiotis Kanavos
        {
162 aa7ac00e Panagiotis Kanavos
            var sync = _syncEvent.WaitAsync();
163 aa7ac00e Panagiotis Kanavos
            var wait = TaskEx.Delay(TimeSpan.FromSeconds(Settings.PollingInterval), NetworkAgent.CancellationToken);
164 aa7ac00e Panagiotis Kanavos
            var signaledTask = await TaskEx.WhenAny(sync, wait);
165 aa7ac00e Panagiotis Kanavos
166 aa7ac00e Panagiotis Kanavos
            //Wait for network processing to finish before polling
167 38ac43a6 Panagiotis Kanavos
            var pauseTask=NetworkAgent.ProceedEvent.WaitAsync();
168 aa7ac00e Panagiotis Kanavos
            await TaskEx.WhenAll(signaledTask, pauseTask);
169 aa7ac00e Panagiotis Kanavos
170 aa7ac00e Panagiotis Kanavos
            //If polling is signalled by SynchNow, ignore the since tag
171 aa7ac00e Panagiotis Kanavos
            if (sync.IsCompleted)
172 aa7ac00e Panagiotis Kanavos
            {
173 aa7ac00e Panagiotis Kanavos
                //TODO: Must convert to AutoReset
174 aa7ac00e Panagiotis Kanavos
                _syncEvent.Reset();
175 aa7ac00e Panagiotis Kanavos
                return null;
176 aa7ac00e Panagiotis Kanavos
            }
177 aa7ac00e Panagiotis Kanavos
            return since;
178 aa7ac00e Panagiotis Kanavos
        }
179 aa7ac00e Panagiotis Kanavos
180 aa7ac00e Panagiotis Kanavos
        public async Task ProcessAccountFiles(AccountInfo accountInfo, DateTime? since = null)
181 aa7ac00e Panagiotis Kanavos
        {
182 aa7ac00e Panagiotis Kanavos
            if (accountInfo == null)
183 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
184 aa7ac00e Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
185 aa7ac00e Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty", "accountInfo");
186 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
187 aa7ac00e Panagiotis Kanavos
188 aa7ac00e Panagiotis Kanavos
189 aa7ac00e Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push(accountInfo.UserName))
190 aa7ac00e Panagiotis Kanavos
            {
191 aa7ac00e Panagiotis Kanavos
                await NetworkAgent.GetDeleteAwaiter();
192 aa7ac00e Panagiotis Kanavos
193 aa7ac00e Panagiotis Kanavos
                Log.Info("Scheduled");
194 aa7ac00e Panagiotis Kanavos
                var client = new CloudFilesClient(accountInfo);
195 aa7ac00e Panagiotis Kanavos
196 99e6329f Panagiotis Kanavos
                //We don't need to check the trash container
197 ec1a1baf Panagiotis Kanavos
                var containers = client.ListContainers(accountInfo.UserName)
198 ec1a1baf Panagiotis Kanavos
                    .Where(c=>c.Name!="trash")
199 ec1a1baf Panagiotis Kanavos
                    .ToList();
200 aa7ac00e Panagiotis Kanavos
201 aa7ac00e Panagiotis Kanavos
202 aa7ac00e Panagiotis Kanavos
                CreateContainerFolders(accountInfo, containers);
203 aa7ac00e Panagiotis Kanavos
204 aa7ac00e Panagiotis Kanavos
                try
205 aa7ac00e Panagiotis Kanavos
                {
206 759bd3c4 Panagiotis Kanavos
                    //Wait for any deletions to finish
207 aa7ac00e Panagiotis Kanavos
                    await NetworkAgent.GetDeleteAwaiter();
208 aa7ac00e Panagiotis Kanavos
                    //Get the poll time now. We may miss some deletions but it's better to keep a file that was deleted
209 aa7ac00e Panagiotis Kanavos
                    //than delete a file that was created while we were executing the poll                    
210 aa7ac00e Panagiotis Kanavos
211 aa7ac00e Panagiotis Kanavos
                    //Get the list of server objects changed since the last check
212 aa7ac00e Panagiotis Kanavos
                    //The name of the container is passed as state in order to create a dictionary of tasks in a subsequent step
213 aa7ac00e Panagiotis Kanavos
                    var listObjects = (from container in containers
214 aa7ac00e Panagiotis Kanavos
                                       select Task<IList<ObjectInfo>>.Factory.StartNew(_ =>
215 aa7ac00e Panagiotis Kanavos
                                             client.ListObjects(accountInfo.UserName, container.Name, since), container.Name)).ToList();
216 99e6329f Panagiotis Kanavos
217 99e6329f Panagiotis Kanavos
                    var listShared = Task<IList<ObjectInfo>>.Factory.StartNew(_ => 
218 99e6329f Panagiotis Kanavos
                        client.ListSharedObjects(since), "shared");
219 aa7ac00e Panagiotis Kanavos
                    listObjects.Add(listShared);
220 aa7ac00e Panagiotis Kanavos
                    var listTasks = await Task.Factory.WhenAll(listObjects.ToArray());
221 aa7ac00e Panagiotis Kanavos
222 aa7ac00e Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["SCHEDULE"].Push("Process Results"))
223 aa7ac00e Panagiotis Kanavos
                    {
224 aa7ac00e Panagiotis Kanavos
                        var dict = listTasks.ToDictionary(t => t.AsyncState);
225 aa7ac00e Panagiotis Kanavos
226 aa7ac00e Panagiotis Kanavos
                        //Get all non-trash objects. Remember, the container name is stored in AsyncState
227 aa7ac00e Panagiotis Kanavos
                        var remoteObjects = from objectList in listTasks
228 aa7ac00e Panagiotis Kanavos
                                            where (string)objectList.AsyncState != "trash"
229 aa7ac00e Panagiotis Kanavos
                                            from obj in objectList.Result
230 aa7ac00e Panagiotis Kanavos
                                            select obj;
231 aa7ac00e Panagiotis Kanavos
232 aa7ac00e Panagiotis Kanavos
                        var sharedObjects = dict["shared"].Result;
233 aa7ac00e Panagiotis Kanavos
234 aa7ac00e Panagiotis Kanavos
                        //DON'T process trashed files
235 aa7ac00e Panagiotis Kanavos
                        //If some files are deleted and added again to a folder, they will be deleted
236 aa7ac00e Panagiotis Kanavos
                        //even though they are new.
237 aa7ac00e Panagiotis Kanavos
                        //We would have to check file dates and hashes to ensure that a trashed file
238 aa7ac00e Panagiotis Kanavos
                        //can be deleted safely from the local hard drive.
239 aa7ac00e Panagiotis Kanavos
                        /*
240 aa7ac00e Panagiotis Kanavos
                        //Items with the same name, hash may be both in the container and the trash
241 aa7ac00e Panagiotis Kanavos
                        //Don't delete items that exist in the container
242 aa7ac00e Panagiotis Kanavos
                        var realTrash = from trash in trashObjects
243 aa7ac00e Panagiotis Kanavos
                                        where
244 aa7ac00e Panagiotis Kanavos
                                            !remoteObjects.Any(
245 aa7ac00e Panagiotis Kanavos
                                                info => info.Name == trash.Name && info.Hash == trash.Hash)
246 aa7ac00e Panagiotis Kanavos
                                        select trash;
247 aa7ac00e Panagiotis Kanavos
                        ProcessTrashedFiles(accountInfo, realTrash);
248 aa7ac00e Panagiotis Kanavos
*/
249 aa7ac00e Panagiotis Kanavos
250 aa7ac00e Panagiotis Kanavos
                        var cleanRemotes = (from info in remoteObjects.Union(sharedObjects)
251 92f18b56 Panagiotis Kanavos
                                            let name = info.Name??""
252 aa7ac00e Panagiotis Kanavos
                                            where !name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase) &&
253 aa7ac00e Panagiotis Kanavos
                                                  !name.StartsWith(FolderConstants.CacheFolder + "/",
254 aa7ac00e Panagiotis Kanavos
                                                                   StringComparison.InvariantCultureIgnoreCase)
255 aa7ac00e Panagiotis Kanavos
                                            select info).ToList();
256 aa7ac00e Panagiotis Kanavos
257 aa7ac00e Panagiotis Kanavos
                        var differencer = _differencer.PostSnapshot(accountInfo, cleanRemotes);
258 aa7ac00e Panagiotis Kanavos
259 ec1a1baf Panagiotis Kanavos
                        ProcessDeletedFiles(accountInfo, differencer.Deleted.FilterDirectlyBelow(SelectiveUris));
260 759bd3c4 Panagiotis Kanavos
261 759bd3c4 Panagiotis Kanavos
                        // @@@ NEED To add previous state here as well, To compare with previous hash
262 759bd3c4 Panagiotis Kanavos
263 759bd3c4 Panagiotis Kanavos
                        
264 aa7ac00e Panagiotis Kanavos
265 aa7ac00e Panagiotis Kanavos
                        //Create a list of actions from the remote files
266 b666b39a Panagiotis Kanavos
                        var allActions = MovesToActions(accountInfo,differencer.Moved.FilterDirectlyBelow(SelectiveUris))
267 b666b39a Panagiotis Kanavos
                                        .Union(
268 b666b39a Panagiotis Kanavos
                                        ChangesToActions(accountInfo, differencer.Changed.FilterDirectlyBelow(SelectiveUris)))
269 aa7ac00e Panagiotis Kanavos
                                        .Union(
270 fec5da06 Panagiotis Kanavos
                                        CreatesToActions(accountInfo, differencer.Created.FilterDirectlyBelow(SelectiveUris)));
271 aa7ac00e Panagiotis Kanavos
272 aa7ac00e Panagiotis Kanavos
                        //And remove those that are already being processed by the agent
273 aa7ac00e Panagiotis Kanavos
                        var distinctActions = allActions
274 aa7ac00e Panagiotis Kanavos
                            .Except(NetworkAgent.GetEnumerable(), new PithosMonitor.LocalFileComparer())
275 aa7ac00e Panagiotis Kanavos
                            .ToList();
276 aa7ac00e Panagiotis Kanavos
277 aa7ac00e Panagiotis Kanavos
                        //Queue all the actions
278 aa7ac00e Panagiotis Kanavos
                        foreach (var message in distinctActions)
279 aa7ac00e Panagiotis Kanavos
                        {
280 aa7ac00e Panagiotis Kanavos
                            NetworkAgent.Post(message);
281 aa7ac00e Panagiotis Kanavos
                        }
282 aa7ac00e Panagiotis Kanavos
283 aa7ac00e Panagiotis Kanavos
                        Log.Info("[LISTENER] End Processing");
284 aa7ac00e Panagiotis Kanavos
                    }
285 aa7ac00e Panagiotis Kanavos
                }
286 aa7ac00e Panagiotis Kanavos
                catch (Exception ex)
287 aa7ac00e Panagiotis Kanavos
                {
288 aa7ac00e Panagiotis Kanavos
                    Log.ErrorFormat("[FAIL] ListObjects for{0} in ProcessRemoteFiles with {1}", accountInfo.UserName, ex);
289 aa7ac00e Panagiotis Kanavos
                    return;
290 aa7ac00e Panagiotis Kanavos
                }
291 aa7ac00e Panagiotis Kanavos
292 aa7ac00e Panagiotis Kanavos
                Log.Info("[LISTENER] Finished");
293 aa7ac00e Panagiotis Kanavos
294 aa7ac00e Panagiotis Kanavos
            }
295 aa7ac00e Panagiotis Kanavos
        }
296 aa7ac00e Panagiotis Kanavos
297 ec1a1baf Panagiotis Kanavos
        readonly AccountsDifferencer _differencer = new AccountsDifferencer();
298 759bd3c4 Panagiotis Kanavos
        private List<Uri> _selectiveUris=new List<Uri>();
299 aa7ac00e Panagiotis Kanavos
300 aa7ac00e Panagiotis Kanavos
        /// <summary>
301 aa7ac00e Panagiotis Kanavos
        /// Deletes local files that are not found in the list of cloud files
302 aa7ac00e Panagiotis Kanavos
        /// </summary>
303 aa7ac00e Panagiotis Kanavos
        /// <param name="accountInfo"></param>
304 aa7ac00e Panagiotis Kanavos
        /// <param name="cloudFiles"></param>
305 ec1a1baf Panagiotis Kanavos
        private void ProcessDeletedFiles(AccountInfo accountInfo, IEnumerable<ObjectInfo> cloudFiles)
306 aa7ac00e Panagiotis Kanavos
        {
307 aa7ac00e Panagiotis Kanavos
            if (accountInfo == null)
308 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
309 aa7ac00e Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
310 aa7ac00e Panagiotis Kanavos
                throw new ArgumentException("The AccountInfo.AccountPath is empty", "accountInfo");
311 aa7ac00e Panagiotis Kanavos
            if (cloudFiles == null)
312 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException("cloudFiles");
313 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
314 aa7ac00e Panagiotis Kanavos
315 aa7ac00e Panagiotis Kanavos
            //On the first run
316 aa7ac00e Panagiotis Kanavos
            if (_firstPoll)
317 aa7ac00e Panagiotis Kanavos
            {
318 aa7ac00e Panagiotis Kanavos
                //Only consider files that are not being modified, ie they are in the Unchanged state            
319 aa7ac00e Panagiotis Kanavos
                var deleteCandidates = FileState.Queryable.Where(state =>
320 aa7ac00e Panagiotis Kanavos
                    state.FilePath.StartsWith(accountInfo.AccountPath)
321 aa7ac00e Panagiotis Kanavos
                    && state.FileStatus == FileStatus.Unchanged).ToList();
322 aa7ac00e Panagiotis Kanavos
323 aa7ac00e Panagiotis Kanavos
324 aa7ac00e Panagiotis Kanavos
                //TODO: filesToDelete must take into account the Others container            
325 aa7ac00e Panagiotis Kanavos
                var filesToDelete = (from deleteCandidate in deleteCandidates
326 aa7ac00e Panagiotis Kanavos
                                     let localFile = FileInfoExtensions.FromPath(deleteCandidate.FilePath)
327 aa7ac00e Panagiotis Kanavos
                                     let relativeFilePath = localFile.AsRelativeTo(accountInfo.AccountPath)
328 aa7ac00e Panagiotis Kanavos
                                     where
329 aa7ac00e Panagiotis Kanavos
                                         !cloudFiles.Any(r => r.RelativeUrlToFilePath(accountInfo.UserName) == relativeFilePath)
330 aa7ac00e Panagiotis Kanavos
                                     select localFile).ToList();
331 aa7ac00e Panagiotis Kanavos
332 aa7ac00e Panagiotis Kanavos
333 aa7ac00e Panagiotis Kanavos
334 aa7ac00e Panagiotis Kanavos
                //Set the status of missing files to Conflict
335 aa7ac00e Panagiotis Kanavos
                foreach (var item in filesToDelete)
336 aa7ac00e Panagiotis Kanavos
                {
337 aa7ac00e Panagiotis Kanavos
                    //Try to acquire a gate on the file, to take into account files that have been dequeued
338 aa7ac00e Panagiotis Kanavos
                    //and are being processed
339 aa7ac00e Panagiotis Kanavos
                    using (var gate = NetworkGate.Acquire(item.FullName, NetworkOperation.Deleting))
340 aa7ac00e Panagiotis Kanavos
                    {
341 aa7ac00e Panagiotis Kanavos
                        if (gate.Failed)
342 aa7ac00e Panagiotis Kanavos
                            continue;
343 aa7ac00e Panagiotis Kanavos
                        StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted);
344 aa7ac00e Panagiotis Kanavos
                    }
345 aa7ac00e Panagiotis Kanavos
                }
346 aa7ac00e Panagiotis Kanavos
                UpdateStatus(PithosStatus.HasConflicts);
347 aa7ac00e Panagiotis Kanavos
                StatusNotification.NotifyConflicts(filesToDelete, String.Format("{0} local files are missing from Pithos, possibly because they were deleted", filesToDelete.Count));
348 aa7ac00e Panagiotis Kanavos
                StatusNotification.NotifyForFiles(filesToDelete, String.Format("{0} files were deleted", filesToDelete.Count), TraceLevel.Info);
349 aa7ac00e Panagiotis Kanavos
            }
350 aa7ac00e Panagiotis Kanavos
            else
351 aa7ac00e Panagiotis Kanavos
            {
352 aa7ac00e Panagiotis Kanavos
                var deletedFiles = new List<FileSystemInfo>();
353 aa7ac00e Panagiotis Kanavos
                foreach (var objectInfo in cloudFiles)
354 aa7ac00e Panagiotis Kanavos
                {
355 aa7ac00e Panagiotis Kanavos
                    var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
356 fbbbe99b Panagiotis Kanavos
                    var item = FileAgent.GetFileAgent(accountInfo).GetFileSystemInfo(relativePath);
357 aa7ac00e Panagiotis Kanavos
                    if (item.Exists)
358 aa7ac00e Panagiotis Kanavos
                    {
359 aa7ac00e Panagiotis Kanavos
                        if ((item.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
360 aa7ac00e Panagiotis Kanavos
                        {
361 aa7ac00e Panagiotis Kanavos
                            item.Attributes = item.Attributes & ~FileAttributes.ReadOnly;
362 aa7ac00e Panagiotis Kanavos
363 aa7ac00e Panagiotis Kanavos
                        }
364 aa7ac00e Panagiotis Kanavos
                        item.Delete();
365 aa7ac00e Panagiotis Kanavos
                        DateTime lastDate;
366 aa7ac00e Panagiotis Kanavos
                        _lastSeen.TryRemove(item.FullName, out lastDate);
367 aa7ac00e Panagiotis Kanavos
                        deletedFiles.Add(item);
368 aa7ac00e Panagiotis Kanavos
                    }
369 aa7ac00e Panagiotis Kanavos
                    StatusKeeper.SetFileState(item.FullName, FileStatus.Deleted, FileOverlayStatus.Deleted);
370 aa7ac00e Panagiotis Kanavos
                }
371 aa7ac00e Panagiotis Kanavos
                StatusNotification.NotifyForFiles(deletedFiles, String.Format("{0} files were deleted", deletedFiles.Count), TraceLevel.Info);
372 aa7ac00e Panagiotis Kanavos
            }
373 aa7ac00e Panagiotis Kanavos
374 aa7ac00e Panagiotis Kanavos
        }
375 aa7ac00e Panagiotis Kanavos
376 b666b39a Panagiotis Kanavos
        /// <summary>
377 b666b39a Panagiotis Kanavos
        /// Creates a Sync action for each changed server file
378 b666b39a Panagiotis Kanavos
        /// </summary>
379 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
380 b666b39a Panagiotis Kanavos
        /// <param name="changes"></param>
381 b666b39a Panagiotis Kanavos
        /// <returns></returns>
382 aa7ac00e Panagiotis Kanavos
        private IEnumerable<CloudAction> ChangesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> changes)
383 aa7ac00e Panagiotis Kanavos
        {
384 aa7ac00e Panagiotis Kanavos
            if (changes == null)
385 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException();
386 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
387 fbbbe99b Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
388 aa7ac00e Panagiotis Kanavos
389 aa7ac00e Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
390 aa7ac00e Panagiotis Kanavos
            //over the remote files
391 aa7ac00e Panagiotis Kanavos
            foreach (var objectInfo in changes)
392 aa7ac00e Panagiotis Kanavos
            {
393 aa7ac00e Panagiotis Kanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
394 b666b39a Panagiotis Kanavos
                //If a directory object already exists, we may need to sync it
395 aa7ac00e Panagiotis Kanavos
                if (fileAgent.Exists(relativePath))
396 aa7ac00e Panagiotis Kanavos
                {
397 aa7ac00e Panagiotis Kanavos
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
398 b666b39a Panagiotis Kanavos
                    //We don't need to sync directories
399 aa7ac00e Panagiotis Kanavos
                    if (objectInfo.Content_Type == @"application/directory" && localFile is DirectoryInfo)
400 aa7ac00e Panagiotis Kanavos
                        continue;
401 aa7ac00e Panagiotis Kanavos
                    using (new SessionScope(FlushAction.Never))
402 aa7ac00e Panagiotis Kanavos
                    {
403 aa7ac00e Panagiotis Kanavos
                        var state = StatusKeeper.GetStateByFilePath(localFile.FullName);
404 aa7ac00e Panagiotis Kanavos
                        _lastSeen[localFile.FullName] = DateTime.Now;
405 aa7ac00e Panagiotis Kanavos
                        //Common files should be checked on a per-case basis to detect differences, which is newer
406 aa7ac00e Panagiotis Kanavos
407 aa7ac00e Panagiotis Kanavos
                        yield return new CloudAction(accountInfo, CloudActionType.MustSynch,
408 aa7ac00e Panagiotis Kanavos
                                                     localFile, objectInfo, state, accountInfo.BlockSize,
409 aa7ac00e Panagiotis Kanavos
                                                     accountInfo.BlockHash);
410 aa7ac00e Panagiotis Kanavos
                    }
411 aa7ac00e Panagiotis Kanavos
                }
412 aa7ac00e Panagiotis Kanavos
                else
413 aa7ac00e Panagiotis Kanavos
                {
414 aa7ac00e Panagiotis Kanavos
                    //Remote files should be downloaded
415 aa7ac00e Panagiotis Kanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo);
416 aa7ac00e Panagiotis Kanavos
                }
417 aa7ac00e Panagiotis Kanavos
            }
418 aa7ac00e Panagiotis Kanavos
        }
419 aa7ac00e Panagiotis Kanavos
420 b666b39a Panagiotis Kanavos
        /// <summary>
421 b666b39a Panagiotis Kanavos
        /// Creates a Local Move action for each moved server file
422 b666b39a Panagiotis Kanavos
        /// </summary>
423 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
424 b666b39a Panagiotis Kanavos
        /// <param name="moves"></param>
425 b666b39a Panagiotis Kanavos
        /// <returns></returns>
426 b666b39a Panagiotis Kanavos
        private IEnumerable<CloudAction> MovesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> moves)
427 b666b39a Panagiotis Kanavos
        {
428 b666b39a Panagiotis Kanavos
            if (moves == null)
429 b666b39a Panagiotis Kanavos
                throw new ArgumentNullException();
430 b666b39a Panagiotis Kanavos
            Contract.EndContractBlock();
431 b666b39a Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
432 b666b39a Panagiotis Kanavos
433 b666b39a Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
434 b666b39a Panagiotis Kanavos
            //over the remote files
435 b666b39a Panagiotis Kanavos
            foreach (var objectInfo in moves)
436 b666b39a Panagiotis Kanavos
            {
437 b666b39a Panagiotis Kanavos
                var previousRelativepath = objectInfo.Previous.RelativeUrlToFilePath(accountInfo.UserName);
438 b666b39a Panagiotis Kanavos
                //If the previous file already exists, we can execute a Move operation
439 b666b39a Panagiotis Kanavos
                if (fileAgent.Exists(previousRelativepath))
440 b666b39a Panagiotis Kanavos
                {
441 b666b39a Panagiotis Kanavos
                    var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
442 b666b39a Panagiotis Kanavos
                    using (new SessionScope(FlushAction.Never))
443 b666b39a Panagiotis Kanavos
                    {
444 b666b39a Panagiotis Kanavos
                        var state = StatusKeeper.GetStateByFilePath(previousFile.FullName);
445 b666b39a Panagiotis Kanavos
                        _lastSeen[previousFile.FullName] = DateTime.Now;
446 b666b39a Panagiotis Kanavos
447 b666b39a Panagiotis Kanavos
                        //For each moved object we need to move both the local file and update                                                
448 b666b39a Panagiotis Kanavos
                        yield return new CloudAction(accountInfo, CloudActionType.RenameLocal,
449 b666b39a Panagiotis Kanavos
                                                     previousFile, objectInfo, state, accountInfo.BlockSize,
450 b666b39a Panagiotis Kanavos
                                                     accountInfo.BlockHash);
451 b666b39a Panagiotis Kanavos
                        //For modified files, we need to download the changes as well
452 b666b39a Panagiotis Kanavos
                        if (objectInfo.Hash!=objectInfo.PreviousHash)
453 b666b39a Panagiotis Kanavos
                            yield return new CloudDownloadAction(accountInfo,objectInfo);
454 b666b39a Panagiotis Kanavos
                    }
455 b666b39a Panagiotis Kanavos
                }
456 b666b39a Panagiotis Kanavos
                //If the previous file does not exist, we need to download it in the new location
457 b666b39a Panagiotis Kanavos
                else
458 b666b39a Panagiotis Kanavos
                {
459 b666b39a Panagiotis Kanavos
                    //Remote files should be downloaded
460 b666b39a Panagiotis Kanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo);
461 b666b39a Panagiotis Kanavos
                }
462 b666b39a Panagiotis Kanavos
            }
463 b666b39a Panagiotis Kanavos
        }
464 b666b39a Panagiotis Kanavos
465 b666b39a Panagiotis Kanavos
466 b666b39a Panagiotis Kanavos
        /// <summary>
467 b666b39a Panagiotis Kanavos
        /// Creates a download action for each new server file
468 b666b39a Panagiotis Kanavos
        /// </summary>
469 b666b39a Panagiotis Kanavos
        /// <param name="accountInfo"></param>
470 b666b39a Panagiotis Kanavos
        /// <param name="creates"></param>
471 b666b39a Panagiotis Kanavos
        /// <returns></returns>
472 aa7ac00e Panagiotis Kanavos
        private IEnumerable<CloudAction> CreatesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> creates)
473 aa7ac00e Panagiotis Kanavos
        {
474 aa7ac00e Panagiotis Kanavos
            if (creates == null)
475 aa7ac00e Panagiotis Kanavos
                throw new ArgumentNullException();
476 aa7ac00e Panagiotis Kanavos
            Contract.EndContractBlock();
477 fbbbe99b Panagiotis Kanavos
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
478 aa7ac00e Panagiotis Kanavos
479 aa7ac00e Panagiotis Kanavos
            //In order to avoid multiple iterations over the files, we iterate only once
480 aa7ac00e Panagiotis Kanavos
            //over the remote files
481 aa7ac00e Panagiotis Kanavos
            foreach (var objectInfo in creates)
482 aa7ac00e Panagiotis Kanavos
            {
483 aa7ac00e Panagiotis Kanavos
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
484 b666b39a Panagiotis Kanavos
                //If the object already exists, we probably have a conflict
485 aa7ac00e Panagiotis Kanavos
                if (fileAgent.Exists(relativePath))
486 aa7ac00e Panagiotis Kanavos
                {
487 aa7ac00e Panagiotis Kanavos
                    //If a directory object already exists, we don't need to perform any other action                    
488 aa7ac00e Panagiotis Kanavos
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
489 aa7ac00e Panagiotis Kanavos
                    StatusKeeper.SetFileState(localFile.FullName, FileStatus.Conflict, FileOverlayStatus.Conflict);
490 aa7ac00e Panagiotis Kanavos
                }
491 aa7ac00e Panagiotis Kanavos
                else
492 aa7ac00e Panagiotis Kanavos
                {
493 aa7ac00e Panagiotis Kanavos
                    //Remote files should be downloaded
494 aa7ac00e Panagiotis Kanavos
                    yield return new CloudDownloadAction(accountInfo, objectInfo);
495 aa7ac00e Panagiotis Kanavos
                }
496 aa7ac00e Panagiotis Kanavos
            }
497 aa7ac00e Panagiotis Kanavos
        }
498 aa7ac00e Panagiotis Kanavos
499 fec5da06 Panagiotis Kanavos
        /// <summary>
500 fec5da06 Panagiotis Kanavos
        /// Notify the UI to update the visual status
501 fec5da06 Panagiotis Kanavos
        /// </summary>
502 fec5da06 Panagiotis Kanavos
        /// <param name="status"></param>
503 aa7ac00e Panagiotis Kanavos
        private void UpdateStatus(PithosStatus status)
504 aa7ac00e Panagiotis Kanavos
        {
505 fec5da06 Panagiotis Kanavos
            try
506 fec5da06 Panagiotis Kanavos
            {
507 fec5da06 Panagiotis Kanavos
                StatusKeeper.SetPithosStatus(status);
508 fec5da06 Panagiotis Kanavos
                StatusNotification.Notify(new Notification());
509 fec5da06 Panagiotis Kanavos
            }
510 fec5da06 Panagiotis Kanavos
            catch (Exception exc)
511 fec5da06 Panagiotis Kanavos
            {
512 fec5da06 Panagiotis Kanavos
                //Failure is not critical, just log it
513 fec5da06 Panagiotis Kanavos
                Log.Warn("Error while updating status", exc);
514 fec5da06 Panagiotis Kanavos
            }
515 aa7ac00e Panagiotis Kanavos
        }
516 aa7ac00e Panagiotis Kanavos
517 aa7ac00e Panagiotis Kanavos
        private static void CreateContainerFolders(AccountInfo accountInfo, IEnumerable<ContainerInfo> containers)
518 aa7ac00e Panagiotis Kanavos
        {
519 aa7ac00e Panagiotis Kanavos
            var containerPaths = from container in containers
520 aa7ac00e Panagiotis Kanavos
                                 let containerPath = Path.Combine(accountInfo.AccountPath, container.Name)
521 aa7ac00e Panagiotis Kanavos
                                 where container.Name != FolderConstants.TrashContainer && !Directory.Exists(containerPath)
522 aa7ac00e Panagiotis Kanavos
                                 select containerPath;
523 aa7ac00e Panagiotis Kanavos
524 aa7ac00e Panagiotis Kanavos
            foreach (var path in containerPaths)
525 aa7ac00e Panagiotis Kanavos
            {
526 aa7ac00e Panagiotis Kanavos
                Directory.CreateDirectory(path);
527 aa7ac00e Panagiotis Kanavos
            }
528 aa7ac00e Panagiotis Kanavos
        }
529 759bd3c4 Panagiotis Kanavos
530 fec5da06 Panagiotis Kanavos
        public void SetSyncUris(Uri[] uris)
531 fec5da06 Panagiotis Kanavos
        {            
532 fec5da06 Panagiotis Kanavos
            SelectiveUris=uris.ToList();
533 759bd3c4 Panagiotis Kanavos
        }
534 759bd3c4 Panagiotis Kanavos
535 759bd3c4 Panagiotis Kanavos
        protected List<Uri> SelectiveUris
536 759bd3c4 Panagiotis Kanavos
        {
537 759bd3c4 Panagiotis Kanavos
            get { return _selectiveUris;}
538 759bd3c4 Panagiotis Kanavos
            set { _selectiveUris = value; }
539 759bd3c4 Panagiotis Kanavos
        }
540 fec5da06 Panagiotis Kanavos
541 fec5da06 Panagiotis Kanavos
        public void AddAccount(AccountInfo accountInfo)
542 fec5da06 Panagiotis Kanavos
        {
543 ec1a1baf Panagiotis Kanavos
            //Avoid adding a duplicate accountInfo
544 ec1a1baf Panagiotis Kanavos
            _accounts.TryAdd(accountInfo.UserName, accountInfo);
545 ec1a1baf Panagiotis Kanavos
        }
546 ec1a1baf Panagiotis Kanavos
547 ec1a1baf Panagiotis Kanavos
        public void RemoveAccount(AccountInfo accountInfo)
548 ec1a1baf Panagiotis Kanavos
        {
549 ec1a1baf Panagiotis Kanavos
            AccountInfo account;
550 ec1a1baf Panagiotis Kanavos
            _accounts.TryRemove(accountInfo.UserName,out account);
551 fec5da06 Panagiotis Kanavos
        }
552 aa7ac00e Panagiotis Kanavos
    }
553 aa7ac00e Panagiotis Kanavos
}