Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / DeleteAgent.cs @ d21f3c77

History | View | Annotate | Download (9.9 kB)

1 255f5f86 Panagiotis Kanavos
#region
2 255f5f86 Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 255f5f86 Panagiotis Kanavos
 * <copyright file="DeleteAgent.cs" company="GRNet">
4 255f5f86 Panagiotis Kanavos
 * 
5 255f5f86 Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 255f5f86 Panagiotis Kanavos
 *
7 255f5f86 Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 255f5f86 Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 255f5f86 Panagiotis Kanavos
 * conditions are met:
10 255f5f86 Panagiotis Kanavos
 *
11 255f5f86 Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 255f5f86 Panagiotis Kanavos
 *      disclaimer.
14 255f5f86 Panagiotis Kanavos
 *
15 255f5f86 Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 255f5f86 Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 255f5f86 Panagiotis Kanavos
 *      provided with the distribution.
19 255f5f86 Panagiotis Kanavos
 *
20 255f5f86 Panagiotis Kanavos
 *
21 255f5f86 Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 255f5f86 Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 255f5f86 Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 255f5f86 Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 255f5f86 Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 255f5f86 Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 255f5f86 Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 255f5f86 Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 255f5f86 Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 255f5f86 Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 255f5f86 Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 255f5f86 Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 255f5f86 Panagiotis Kanavos
 *
34 255f5f86 Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 255f5f86 Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 255f5f86 Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 255f5f86 Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 255f5f86 Panagiotis Kanavos
 * </copyright>
39 255f5f86 Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 255f5f86 Panagiotis Kanavos
 */
41 255f5f86 Panagiotis Kanavos
#endregion
42 3742088d Panagiotis Kanavos
using System.Collections.Concurrent;
43 3742088d Panagiotis Kanavos
using System.ComponentModel.Composition;
44 3742088d Panagiotis Kanavos
using System.Diagnostics.Contracts;
45 3742088d Panagiotis Kanavos
using System.IO;
46 3742088d Panagiotis Kanavos
using System.Net;
47 db8a9589 Panagiotis Kanavos
using System.Reflection;
48 3742088d Panagiotis Kanavos
using System.Threading.Tasks.Dataflow;
49 3742088d Panagiotis Kanavos
using Pithos.Interfaces;
50 3742088d Panagiotis Kanavos
using Pithos.Network;
51 3742088d Panagiotis Kanavos
using System;
52 3742088d Panagiotis Kanavos
using log4net;
53 3742088d Panagiotis Kanavos
54 3742088d Panagiotis Kanavos
namespace Pithos.Core.Agents
55 3742088d Panagiotis Kanavos
{
56 3742088d Panagiotis Kanavos
57 3742088d Panagiotis Kanavos
    /// <summary>
58 3742088d Panagiotis Kanavos
    /// The Delete Agent is used to delete files from the Pithos server with high priority, 
59 38ac43a6 Panagiotis Kanavos
    /// blocking the network agent through the ProceedEvent until all pending deletions complete
60 3742088d Panagiotis Kanavos
    /// </summary>    
61 3742088d Panagiotis Kanavos
    [Export]
62 3742088d Panagiotis Kanavos
    public class DeleteAgent
63 3742088d Panagiotis Kanavos
    {
64 db8a9589 Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
65 3742088d Panagiotis Kanavos
66 3742088d Panagiotis Kanavos
        [Import]
67 3742088d Panagiotis Kanavos
        public IStatusKeeper StatusKeeper { get; set; }
68 70e0b702 Panagiotis Kanavos
        
69 70e0b702 Panagiotis Kanavos
        public IStatusNotification StatusNotification { get; set; }
70 3742088d Panagiotis Kanavos
71 3742088d Panagiotis Kanavos
        //A separate agent is used to execute delete actions immediatelly;
72 3742088d Panagiotis Kanavos
        private readonly ActionBlock<CloudDeleteAction> _deleteAgent;
73 3742088d Panagiotis Kanavos
74 38ac43a6 Panagiotis Kanavos
        //The Proceed event signals the the network agent to proceed with processing.
75 38ac43a6 Panagiotis Kanavos
        //Essentially this event pauses the network agent to give priority to the deletion agent
76 3742088d Panagiotis Kanavos
        //Initially the event is signalled because we don't need to pause
77 38ac43a6 Panagiotis Kanavos
        private readonly AsyncManualResetEvent _proceedEvent = new AsyncManualResetEvent(true);
78 3742088d Panagiotis Kanavos
79 38ac43a6 Panagiotis Kanavos
        public AsyncManualResetEvent ProceedEvent
80 3742088d Panagiotis Kanavos
        {
81 38ac43a6 Panagiotis Kanavos
            get { return _proceedEvent; }
82 3742088d Panagiotis Kanavos
        }
83 3742088d Panagiotis Kanavos
84 3742088d Panagiotis Kanavos
        //Deleted file names are stored in memory so we can check that a file has already been deleted.
85 3742088d Panagiotis Kanavos
        //and avoid sending duplicate delete commands
86 3742088d Panagiotis Kanavos
        readonly ConcurrentDictionary<string, DateTime> _deletedFiles = new ConcurrentDictionary<string, DateTime>();
87 3742088d Panagiotis Kanavos
88 3742088d Panagiotis Kanavos
        public DeleteAgent()
89 3742088d Panagiotis Kanavos
        {
90 3742088d Panagiotis Kanavos
            _deleteAgent = new ActionBlock<CloudDeleteAction>(message => ProcessDelete(message), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });
91 3742088d Panagiotis Kanavos
92 3742088d Panagiotis Kanavos
        }
93 3742088d Panagiotis Kanavos
94 3742088d Panagiotis Kanavos
        /// <summary>
95 3742088d Panagiotis Kanavos
        /// Processes cloud delete actions
96 3742088d Panagiotis Kanavos
        /// </summary>
97 3742088d Panagiotis Kanavos
        /// <param name="action">The delete action to execute</param>
98 3742088d Panagiotis Kanavos
        /// <returns></returns>
99 3742088d Panagiotis Kanavos
        /// <remarks>
100 3742088d Panagiotis Kanavos
        /// When a file/folder is deleted locally, we must delete it ASAP from the server and block any download
101 3742088d Panagiotis Kanavos
        /// operations that may be in progress.
102 3742088d Panagiotis Kanavos
        /// <para>
103 3742088d Panagiotis Kanavos
        /// A separate agent is used to process deletes because the main agent may be busy with a long operation.
104 3742088d Panagiotis Kanavos
        /// </para>
105 3742088d Panagiotis Kanavos
        /// </remarks>
106 3742088d Panagiotis Kanavos
        private void ProcessDelete(CloudDeleteAction action)
107 3742088d Panagiotis Kanavos
        {
108 3742088d Panagiotis Kanavos
            if (action == null)
109 3742088d Panagiotis Kanavos
                throw new ArgumentNullException("action");
110 3742088d Panagiotis Kanavos
            if (action.AccountInfo == null)
111 3742088d Panagiotis Kanavos
                throw new ArgumentException("The action.AccountInfo is empty", "action");
112 3742088d Panagiotis Kanavos
            Contract.EndContractBlock();
113 3742088d Panagiotis Kanavos
114 3742088d Panagiotis Kanavos
            var accountInfo = action.AccountInfo;
115 3742088d Panagiotis Kanavos
116 6bcdd8e2 Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Operation"].Push("ProcessDelete"))
117 3742088d Panagiotis Kanavos
            {
118 3742088d Panagiotis Kanavos
                Log.InfoFormat("[ACTION] Start Processing {0}", action);
119 3742088d Panagiotis Kanavos
120 3742088d Panagiotis Kanavos
                var cloudFile = action.CloudFile;
121 3742088d Panagiotis Kanavos
122 3742088d Panagiotis Kanavos
                try
123 3742088d Panagiotis Kanavos
                {
124 3742088d Panagiotis Kanavos
                    //Acquire a lock on the deleted file to prevent uploading/downloading operations from the normal
125 3742088d Panagiotis Kanavos
                    //agent
126 3742088d Panagiotis Kanavos
                    using (NetworkGate.Acquire(action.LocalFile.FullName, NetworkOperation.Deleting))
127 3742088d Panagiotis Kanavos
                    {
128 3742088d Panagiotis Kanavos
129 3742088d Panagiotis Kanavos
                        //Add the file URL to the deleted files list
130 3742088d Panagiotis Kanavos
                        var key = GetFileKey(action.CloudFile);
131 3742088d Panagiotis Kanavos
                        _deletedFiles[key] = DateTime.Now;
132 3742088d Panagiotis Kanavos
133 38ac43a6 Panagiotis Kanavos
                        _proceedEvent.Reset();
134 3742088d Panagiotis Kanavos
                        // and then delete the file from the server
135 3742088d Panagiotis Kanavos
                        DeleteCloudFile(accountInfo, cloudFile);
136 3742088d Panagiotis Kanavos
137 3742088d Panagiotis Kanavos
                        Log.InfoFormat("[ACTION] End Delete {0}:{1}->{2}", action.Action, action.LocalFile,
138 3742088d Panagiotis Kanavos
                                       action.CloudFile.Name);
139 3742088d Panagiotis Kanavos
                    }
140 3742088d Panagiotis Kanavos
                }
141 3742088d Panagiotis Kanavos
                catch (WebException exc)
142 3742088d Panagiotis Kanavos
                {
143 3742088d Panagiotis Kanavos
                    Log.ErrorFormat("[WEB ERROR] {0} : {1} -> {2} due to exception\r\n{3}", action.Action, action.LocalFile, action.CloudFile, exc);
144 3742088d Panagiotis Kanavos
                }
145 3742088d Panagiotis Kanavos
                catch (OperationCanceledException)
146 3742088d Panagiotis Kanavos
                {
147 3742088d Panagiotis Kanavos
                    throw;
148 3742088d Panagiotis Kanavos
                }
149 3742088d Panagiotis Kanavos
                catch (DirectoryNotFoundException)
150 3742088d Panagiotis Kanavos
                {
151 3742088d Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the directory was not found.\n Rescheduling a delete",
152 3742088d Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
153 3742088d Panagiotis Kanavos
                    //Repost a delete action for the missing file
154 3742088d Panagiotis Kanavos
                    _deleteAgent.Post(action);
155 3742088d Panagiotis Kanavos
                }
156 3742088d Panagiotis Kanavos
                catch (FileNotFoundException)
157 3742088d Panagiotis Kanavos
                {
158 3742088d Panagiotis Kanavos
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the file was not found.\n Rescheduling a delete",
159 3742088d Panagiotis Kanavos
                        action.Action, action.LocalFile, action.CloudFile);
160 3742088d Panagiotis Kanavos
                    //Post a delete action for the missing file
161 3742088d Panagiotis Kanavos
                    _deleteAgent.Post(action);
162 3742088d Panagiotis Kanavos
                }
163 3742088d Panagiotis Kanavos
                catch (Exception exc)
164 3742088d Panagiotis Kanavos
                {
165 3742088d Panagiotis Kanavos
                    Log.ErrorFormat("[REQUEUE] {0} : {1} -> {2} due to exception\r\n{3}",
166 3742088d Panagiotis Kanavos
                                     action.Action, action.LocalFile, action.CloudFile, exc);
167 3742088d Panagiotis Kanavos
168 3742088d Panagiotis Kanavos
                    _deleteAgent.Post(action);
169 3742088d Panagiotis Kanavos
                }
170 3742088d Panagiotis Kanavos
                finally
171 3742088d Panagiotis Kanavos
                {
172 3742088d Panagiotis Kanavos
                    //Set the event when all delete actions are processed
173 3742088d Panagiotis Kanavos
                    if (_deleteAgent.InputCount == 0)
174 38ac43a6 Panagiotis Kanavos
                        _proceedEvent.Set();
175 3742088d Panagiotis Kanavos
176 3742088d Panagiotis Kanavos
                }
177 3742088d Panagiotis Kanavos
            }
178 3742088d Panagiotis Kanavos
        }
179 3742088d Panagiotis Kanavos
180 3742088d Panagiotis Kanavos
        //Returns true if an action concerns a file that was deleted
181 3742088d Panagiotis Kanavos
        public bool IsDeletedFile(CloudAction action)
182 3742088d Panagiotis Kanavos
        {
183 3742088d Panagiotis Kanavos
            //Doesn't work for actions targeting shared files
184 3742088d Panagiotis Kanavos
            if (action.IsShared)
185 3742088d Panagiotis Kanavos
                return false;
186 3742088d Panagiotis Kanavos
            var key = GetFileKey(action.CloudFile);
187 3742088d Panagiotis Kanavos
            DateTime entryDate;
188 3742088d Panagiotis Kanavos
            if (_deletedFiles.TryGetValue(key, out entryDate))
189 3742088d Panagiotis Kanavos
            {
190 3742088d Panagiotis Kanavos
                //If the delete entry was created after this action, abort the action
191 3742088d Panagiotis Kanavos
                if (entryDate > action.Created)
192 3742088d Panagiotis Kanavos
                    return true;
193 3742088d Panagiotis Kanavos
                //Otherwise, remove the stale entry 
194 3742088d Panagiotis Kanavos
                _deletedFiles.TryRemove(key, out entryDate);
195 3742088d Panagiotis Kanavos
            }
196 3742088d Panagiotis Kanavos
            return false;
197 3742088d Panagiotis Kanavos
        }
198 3742088d Panagiotis Kanavos
199 3742088d Panagiotis Kanavos
        public void Post(CloudDeleteAction action)
200 3742088d Panagiotis Kanavos
        {
201 3742088d Panagiotis Kanavos
            _deleteAgent.Post(action);
202 3742088d Panagiotis Kanavos
        }
203 3742088d Panagiotis Kanavos
204 3742088d Panagiotis Kanavos
        private void DeleteCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile)
205 3742088d Panagiotis Kanavos
        {
206 3742088d Panagiotis Kanavos
            if (accountInfo == null)
207 3742088d Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
208 3742088d Panagiotis Kanavos
            if (cloudFile == null)
209 3742088d Panagiotis Kanavos
                throw new ArgumentNullException("cloudFile");
210 3742088d Panagiotis Kanavos
211 3742088d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cloudFile.Container))
212 3742088d Panagiotis Kanavos
                throw new ArgumentException("Invalid container", "cloudFile");
213 3742088d Panagiotis Kanavos
            Contract.EndContractBlock();
214 3742088d Panagiotis Kanavos
215 3742088d Panagiotis Kanavos
            var fileAgent = GetFileAgent(accountInfo);
216 3742088d Panagiotis Kanavos
217 6bcdd8e2 Panagiotis Kanavos
            using (ThreadContext.Stacks["Operation"].Push("DeleteCloudFile"))
218 3742088d Panagiotis Kanavos
            {
219 3742088d Panagiotis Kanavos
                var fileName = cloudFile.RelativeUrlToFilePath(accountInfo.UserName);
220 3742088d Panagiotis Kanavos
                var info = fileAgent.GetFileSystemInfo(fileName);
221 3742088d Panagiotis Kanavos
                var fullPath = info.FullName.ToLower();
222 3742088d Panagiotis Kanavos
223 3742088d Panagiotis Kanavos
                StatusKeeper.SetFileOverlayStatus(fullPath, FileOverlayStatus.Modified);
224 3742088d Panagiotis Kanavos
225 3742088d Panagiotis Kanavos
                var account = cloudFile.Account ?? accountInfo.UserName;
226 3742088d Panagiotis Kanavos
                var container = cloudFile.Container;//?? FolderConstants.PithosContainer;
227 3742088d Panagiotis Kanavos
228 3742088d Panagiotis Kanavos
                var client = new CloudFilesClient(accountInfo);
229 3742088d Panagiotis Kanavos
                client.DeleteObject(account, container, cloudFile.Name);
230 3742088d Panagiotis Kanavos
231 3742088d Panagiotis Kanavos
                StatusKeeper.ClearFileStatus(fullPath);
232 70e0b702 Panagiotis Kanavos
                StatusNotification.Notify(new CloudNotification{Data=cloudFile});
233 3742088d Panagiotis Kanavos
            }
234 3742088d Panagiotis Kanavos
        }
235 3742088d Panagiotis Kanavos
236 3742088d Panagiotis Kanavos
237 3742088d Panagiotis Kanavos
        private static string GetFileKey(ObjectInfo info)
238 3742088d Panagiotis Kanavos
        {
239 3742088d Panagiotis Kanavos
            var key = String.Format("{0}/{1}/{2}", info.Account, info.Container, info.Name);
240 3742088d Panagiotis Kanavos
            return key;
241 3742088d Panagiotis Kanavos
        }
242 3742088d Panagiotis Kanavos
243 3742088d Panagiotis Kanavos
        private static FileAgent GetFileAgent(AccountInfo accountInfo)
244 3742088d Panagiotis Kanavos
        {
245 3742088d Panagiotis Kanavos
            return AgentLocator<FileAgent>.Get(accountInfo.AccountPath);
246 3742088d Panagiotis Kanavos
        }
247 3742088d Panagiotis Kanavos
248 3742088d Panagiotis Kanavos
    }
249 3742088d Panagiotis Kanavos
}