Added header to all files. Closes #2064
[pithos-ms-client] / trunk / Pithos.Core / FileState.cs
1 #region
2 /* -----------------------------------------------------------------------
3  * <copyright file="FileState.cs" company="GRNet">
4  * 
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or
8  * without modification, are permitted provided that the following
9  * conditions are met:
10  *
11  *   1. Redistributions of source code must retain the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer.
14  *
15  *   2. Redistributions in binary form must reproduce the above
16  *      copyright notice, this list of conditions and the following
17  *      disclaimer in the documentation and/or other materials
18  *      provided with the distribution.
19  *
20  *
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * The views and conclusions contained in the software and
35  * documentation are those of the authors and should not be
36  * interpreted as representing official policies, either expressed
37  * or implied, of GRNET S.A.
38  * </copyright>
39  * -----------------------------------------------------------------------
40  */
41 #endregion
42 using System.Diagnostics.Contracts;
43 using System.IO;
44 using System.Threading.Tasks;
45 using Castle.ActiveRecord;
46 using Castle.ActiveRecord.Framework;
47 using Pithos.Core.Agents;
48 using Pithos.Interfaces;
49 using Pithos.Network;
50 using log4net;
51
52 namespace Pithos.Core
53 {
54     using System;
55     using System.Collections.Generic;
56     using System.Linq;
57
58     /// <summary>
59     /// TODO: Update summary.
60     /// </summary>
61     [ActiveRecord]
62     public class FileState : ActiveRecordLinqBase<FileState>
63     {
64         private static readonly ILog Log = LogManager.GetLogger("FileState");
65
66         private IList<FileTag> _tags = new List<FileTag>();
67
68         [PrimaryKey(PrimaryKeyType.Guid)]
69         public Guid Id { get; set; }
70
71         
72         [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
73         public string FilePath { get; set; }
74
75         [Property]
76         public FileOverlayStatus OverlayStatus { get; set; }
77
78         [Property]
79         public FileStatus FileStatus { get; set; }
80
81         [Property]
82         public string Checksum { get; set; }
83
84
85         [Property]
86         public long? Version { get; set; }
87
88         [Property]
89         public DateTime? VersionTimeStamp { get; set; }
90
91
92         [Property]
93         public bool IsShared { get; set; }
94
95         [Property]
96         public string SharedBy { get; set; }
97
98         [Property]
99         public bool ShareWrite { get; set; }
100
101         [Property]
102         public bool IsFolder{ get; set; }
103
104         [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]
105         public IList<FileTag> Tags
106         {
107             get { return _tags; }
108             set { _tags = value; }
109         }
110
111         [Property]
112         public DateTime Modified { get; set; }
113
114
115         public FileSystemInfo GetFileSystemInfo()
116         {
117             if (String.IsNullOrWhiteSpace(FilePath))
118                 throw new InvalidOperationException();
119             Contract.EndContractBlock();
120
121             return Directory.Exists(FilePath) ?
122                 (FileSystemInfo)new DirectoryInfo(FilePath)
123                 : new FileInfo(FilePath);
124         }
125
126         public string GetRelativeUrl(AccountInfo accountInfo)
127         {
128             if (accountInfo==null)
129                 throw new ArgumentNullException("accountInfo");
130             Contract.EndContractBlock();
131
132             var fsi=GetFileSystemInfo();
133             return fsi.AsRelativeUrlTo(accountInfo.AccountPath);
134         }
135         /*public static FileState FindByFilePath(string absolutePath)
136         {
137             if (string.IsNullOrWhiteSpace(absolutePath))
138                 throw new ArgumentNullException("absolutePath");
139             Contract.EndContractBlock();
140             try
141             {
142
143                 
144
145                 return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);
146             }
147             catch (Exception ex)
148             {
149                 Log.Error(ex.ToString());
150                 throw;
151             }
152
153
154         }*/
155
156        /* public static void DeleteByFilePath(string absolutePath)
157         {
158             if (string.IsNullOrWhiteSpace(absolutePath))
159                 throw new ArgumentNullException("absolutePath");
160             Contract.EndContractBlock();
161
162             ExecuteWithRetry((session, instance) =>
163                         {
164                             const string hqlDelete = "delete FileState where FilePath = :path";
165                             var deletedEntities = session.CreateQuery(hqlDelete)
166                                 .SetString("path", absolutePath)
167                                 .ExecuteUpdate();
168                             return deletedEntities;
169                         }, null);
170
171         }*/
172
173         public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
174         {
175             if (string.IsNullOrWhiteSpace(absolutePath))
176                 throw new ArgumentNullException("absolutePath");
177             Contract.EndContractBlock();
178
179             ExecuteWithRetry((session, instance) =>
180                         {
181                             const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
182                             var updatedEntities = session.CreateQuery(hqlUpdate)
183                                 .SetString("path", absolutePath)
184                                 .SetEnum("status", newStatus)
185                                 .ExecuteUpdate();
186                             if (updatedEntities == 0)
187                             {
188                                 var newState = new FileState
189                                                    {
190                                                        FilePath = absolutePath,
191                                                        Id = Guid.NewGuid(),
192                                                        FileStatus = newStatus,
193                                                        IsFolder=Directory.Exists(absolutePath)
194                                                    };
195                                 newState.CreateAndFlush();
196                             }
197                             return null;
198                         }, null);
199
200         }
201
202         public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
203         {
204             if (string.IsNullOrWhiteSpace(absolutePath))
205                 throw new ArgumentNullException("absolutePath");
206             Contract.EndContractBlock();
207
208             ExecuteWithRetry((session, instance) =>
209                         {
210                             const string hqlUpdate =
211                                 "update FileState set OverlayStatus= :status where FilePath = :path ";
212                             var updatedEntities = session.CreateQuery(hqlUpdate)                                
213                                 .SetString("path", absolutePath)
214                                 .SetEnum("status", newStatus)                                
215                                 .ExecuteUpdate();
216                             if (updatedEntities == 0)
217                             {
218                                 var newState = new FileState
219                                                    {
220                                                        FilePath = absolutePath,
221                                                        Id = Guid.NewGuid(),
222                                                        OverlayStatus = newStatus,
223                                                        IsFolder=Directory.Exists(absolutePath)
224                                                    };
225                                 newState.CreateAndFlush();
226                             }
227                             return null;
228                         }, null);
229
230         }
231
232 /*
233         public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
234         {
235             if (string.IsNullOrWhiteSpace(absolutePath))
236                 throw new ArgumentNullException("absolutePath");
237             Contract.EndContractBlock();
238
239             ExecuteWithRetry((session, instance) =>
240                         {
241                             const string hqlUpdate =
242                                 "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path  ";
243                             var updatedEntities = session.CreateQuery(hqlUpdate)
244                                 .SetString("path", absolutePath)
245                                 .SetEnum("fileStatus", fileStatus)
246                                 .SetEnum("overlayStatus", overlayStatus)
247                                 .ExecuteUpdate();
248                             return updatedEntities;
249                         }, null);
250
251         }
252 */
253
254 /*
255         public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
256         {
257             if (string.IsNullOrWhiteSpace(absolutePath))
258                 throw new ArgumentNullException("absolutePath");
259             Contract.EndContractBlock();
260
261             ExecuteWithRetry((session, instance) =>
262                         {
263                             const string hqlUpdate =
264                                 "update FileState set FileStatus= :fileStatus where FilePath = :path  ";
265                             var updatedEntities = session.CreateQuery(hqlUpdate)
266                                 .SetString("path", absolutePath)
267                                 .SetEnum("fileStatus", fileStatus)
268                                 .ExecuteUpdate();
269                             return updatedEntities;
270                         }, null);
271
272         }
273
274 */
275         public static void RenameState(string oldPath, string newPath)
276         {
277             if (string.IsNullOrWhiteSpace(oldPath))
278                 throw new ArgumentNullException("oldPath");
279             Contract.EndContractBlock();
280
281             ExecuteWithRetry((session, instance) =>
282                         {
283                             const string hqlUpdate =
284                                 "update FileState set FilePath= :newPath where FilePath = :oldPath ";
285                             var updatedEntities = session.CreateQuery(hqlUpdate)
286                                 .SetString("oldPath", oldPath)
287                                 .SetString("newPath", newPath)
288                                 .ExecuteUpdate();
289                             return updatedEntities;
290                         }, null);
291
292         }
293
294      /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)
295         {
296
297             ExecuteWithRetry((session, instance) =>
298             {
299                 const string hqlUpdate =
300                     "update FileState set FileStatus= :fileStatus where Id = :id  ";
301                 var updatedEntities = session.CreateQuery(hqlUpdate)
302                     .SetGuid("id", id)
303                     .SetEnum("fileStatus", fileStatus)
304                     .ExecuteUpdate();
305                 return updatedEntities;
306             }, null);
307         }*/
308
309         public static void UpdateChecksum(string absolutePath, string checksum)
310         {
311             if (string.IsNullOrWhiteSpace(absolutePath))
312                 throw new ArgumentNullException("absolutePath");
313             Contract.EndContractBlock();
314
315             ExecuteWithRetry((session, instance) =>
316                         {
317                             const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
318                             var updatedEntities = session.CreateQuery(hqlUpdate)
319                                 .SetString("path", absolutePath)
320                                 .SetString("checksum", checksum)
321                                 .ExecuteUpdate();
322                             return updatedEntities;
323                         }, null);
324
325         }
326
327         public static void ChangeRootPath(string oldPath, string newPath)
328         {
329             if (String.IsNullOrWhiteSpace(oldPath))
330                 throw new ArgumentNullException("oldPath");
331             if (!Path.IsPathRooted(oldPath))
332                 throw new ArgumentException("oldPath must be an absolute path", "oldPath");
333             if (string.IsNullOrWhiteSpace(newPath))
334                 throw new ArgumentNullException("newPath");
335             if (!Path.IsPathRooted(newPath))
336                 throw new ArgumentException("newPath must be an absolute path", "newPath");
337             Contract.EndContractBlock();
338
339             //Ensure the paths end with the same character
340             if (!oldPath.EndsWith("\\"))
341                 oldPath = oldPath + "\\";
342             if (!newPath.EndsWith("\\"))
343                 newPath = newPath + "\\";
344
345                 ExecuteWithRetry((session, instance) =>
346                             {
347                                 const string hqlUpdate =
348                                     "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
349                                 var renames = session.CreateQuery(hqlUpdate)
350                                     .SetString("oldPath", oldPath)
351                                     .SetString("newPath", newPath)
352                                     .ExecuteUpdate();
353                                 return renames;
354                             }, null);
355         }
356
357         public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
358         {
359             if (blockSize <= 0)
360                 throw new ArgumentOutOfRangeException("blockSize");
361             if (String.IsNullOrWhiteSpace(algorithm))
362                 throw new ArgumentNullException("algorithm");
363             Contract.EndContractBlock();
364
365
366             var fileState = new FileState
367                                 {
368                                     FilePath = filePath,
369                                     OverlayStatus = FileOverlayStatus.Unversioned,
370                                     FileStatus = FileStatus.Created,
371                                     Id = Guid.NewGuid()
372                                 };
373
374
375             return fileState.UpdateHashesAsync(blockSize, algorithm);
376         }
377
378         public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm)
379         {
380             if (blockSize <= 0)
381                 throw new ArgumentOutOfRangeException("blockSize");
382             if (String.IsNullOrWhiteSpace(algorithm))
383                 throw new ArgumentNullException("algorithm");
384             Contract.EndContractBlock();
385
386             //Skip updating the hash for folders
387             if (Directory.Exists(FilePath))
388                 return this;
389
390             var hash = await TaskEx.Run(() =>
391                                             {
392                                                 var info = new FileInfo(FilePath);
393                                                 return info.CalculateHash(blockSize, algorithm);
394                                             });
395
396             Checksum = hash;
397
398             return this;
399         }
400
401         private static void ExecuteWithRetry(NHibernateDelegate call, object state)
402         {
403             int retries = 3;
404             while (retries > 0)
405                 try
406                 {
407                     using (new SessionScope())
408                     {
409                         Execute(call, state);
410                         return;
411                     }
412                 }
413                 catch (ActiveRecordException )
414                 {
415                     retries--;
416                     if (retries <= 0)
417                         throw;
418                 }
419         }
420     }
421
422     [ActiveRecord("Tags")]
423     public class FileTag : ActiveRecordLinqBase<FileTag>
424     {
425         [PrimaryKey]
426         public int Id { get; set; }
427
428         [Property]
429         public string Name { get; set; }
430
431         [Property]
432         public string Value { get; set; }
433
434         [BelongsTo("FileStateId")]
435         public FileState FileState { get; set; }
436
437     }
438    
439 }