1 // -----------------------------------------------------------------------
2 // <copyright file="FileState.cs" company="GRNet">
3 // Copyright 2011 GRNET S.A. All rights reserved.
5 // Redistribution and use in source and binary forms, with or
6 // without modification, are permitted provided that the following
9 // 1. Redistributions of source code must retain the above
10 // copyright notice, this list of conditions and the following
13 // 2. Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following
15 // disclaimer in the documentation and/or other materials
16 // provided with the distribution.
18 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
31 // The views and conclusions contained in the software and
32 // documentation are those of the authors and should not be
33 // interpreted as representing official policies, either expressed
34 // or implied, of GRNET S.A.
36 // -----------------------------------------------------------------------
38 using System.Data.SQLite;
39 using System.Diagnostics.Contracts;
41 using System.Threading.Tasks;
42 using Castle.ActiveRecord;
43 using Castle.ActiveRecord.Framework;
44 using Pithos.Core.Agents;
45 using Pithos.Interfaces;
51 using System.Collections.Generic;
55 /// TODO: Update summary.
58 public class FileState : ActiveRecordLinqBase<FileState>
60 private static readonly ILog Log = LogManager.GetLogger("FileState");
62 private string _filePath;
63 private IList<FileTag> _tags = new List<FileTag>();
65 [PrimaryKey(PrimaryKeyType.Guid)]
66 public Guid Id { get; set; }
68 [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
69 public string FilePath
71 get { return _filePath; }
72 set { _filePath = value.ToLower(); }
76 public FileOverlayStatus OverlayStatus { get; set; }
79 public FileStatus FileStatus { get; set; }
82 public string Checksum { get; set; }
86 public long? Version { get; set; }
89 public DateTime? VersionTimeStamp { get; set; }
93 public bool IsShared { get; set; }
96 public string SharedBy { get; set; }
99 public bool ShareWrite { get; set; }
102 [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]
103 public IList<FileTag> Tags
105 get { return _tags; }
106 set { _tags = value; }
110 public static FileState FindByFilePath(string absolutePath)
112 if (string.IsNullOrWhiteSpace(absolutePath))
113 throw new ArgumentNullException("absolutePath");
114 Contract.EndContractBlock();
120 return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
124 Log.Error(ex.ToString());
131 /* public static void DeleteByFilePath(string absolutePath)
133 if (string.IsNullOrWhiteSpace(absolutePath))
134 throw new ArgumentNullException("absolutePath");
135 Contract.EndContractBlock();
137 ExecuteWithRetry((session, instance) =>
139 const string hqlDelete = "delete FileState where FilePath = :path";
140 var deletedEntities = session.CreateQuery(hqlDelete)
141 .SetString("path", absolutePath.ToLower())
143 return deletedEntities;
148 public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
150 if (string.IsNullOrWhiteSpace(absolutePath))
151 throw new ArgumentNullException("absolutePath");
152 Contract.EndContractBlock();
154 ExecuteWithRetry((session, instance) =>
156 const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
157 var updatedEntities = session.CreateQuery(hqlUpdate)
158 .SetString("path", absolutePath.ToLower())
159 .SetEnum("status", newStatus)
161 if (updatedEntities == 0)
163 var newState = new FileState
165 FilePath = absolutePath.ToLower(),
167 FileStatus = newStatus
169 newState.CreateAndFlush();
176 public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
178 if (string.IsNullOrWhiteSpace(absolutePath))
179 throw new ArgumentNullException("absolutePath");
180 Contract.EndContractBlock();
182 ExecuteWithRetry((session, instance) =>
184 const string hqlUpdate =
185 "update FileState set OverlayStatus= :status where FilePath = :path ";
186 var updatedEntities = session.CreateQuery(hqlUpdate)
187 .SetString("path", absolutePath.ToLower())
188 .SetEnum("status", newStatus)
190 if (updatedEntities == 0)
192 var newState = new FileState
194 FilePath = absolutePath,
196 OverlayStatus = newStatus
198 newState.CreateAndFlush();
206 public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
208 if (string.IsNullOrWhiteSpace(absolutePath))
209 throw new ArgumentNullException("absolutePath");
210 Contract.EndContractBlock();
212 ExecuteWithRetry((session, instance) =>
214 const string hqlUpdate =
215 "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path ";
216 var updatedEntities = session.CreateQuery(hqlUpdate)
217 .SetString("path", absolutePath.ToLower())
218 .SetEnum("fileStatus", fileStatus)
219 .SetEnum("overlayStatus", overlayStatus)
221 return updatedEntities;
228 public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
230 if (string.IsNullOrWhiteSpace(absolutePath))
231 throw new ArgumentNullException("absolutePath");
232 Contract.EndContractBlock();
234 ExecuteWithRetry((session, instance) =>
236 const string hqlUpdate =
237 "update FileState set FileStatus= :fileStatus where FilePath = :path ";
238 var updatedEntities = session.CreateQuery(hqlUpdate)
239 .SetString("path", absolutePath.ToLower())
240 .SetEnum("fileStatus", fileStatus)
242 return updatedEntities;
248 public static void RenameState(string oldPath, string newPath)
250 if (string.IsNullOrWhiteSpace(oldPath))
251 throw new ArgumentNullException("oldPath");
252 Contract.EndContractBlock();
254 ExecuteWithRetry((session, instance) =>
256 const string hqlUpdate =
257 "update FileState set FilePath= :newPath where FilePath = :oldPath ";
258 var updatedEntities = session.CreateQuery(hqlUpdate)
259 .SetString("oldPath", oldPath.ToLower())
260 .SetString("newPath", newPath.ToLower())
262 return updatedEntities;
267 /* public static void UpdateStatus(Guid id, FileStatus fileStatus)
270 ExecuteWithRetry((session, instance) =>
272 const string hqlUpdate =
273 "update FileState set FileStatus= :fileStatus where Id = :id ";
274 var updatedEntities = session.CreateQuery(hqlUpdate)
276 .SetEnum("fileStatus", fileStatus)
278 return updatedEntities;
282 public static void UpdateChecksum(string absolutePath, string checksum)
284 if (string.IsNullOrWhiteSpace(absolutePath))
285 throw new ArgumentNullException("absolutePath");
286 Contract.EndContractBlock();
288 ExecuteWithRetry((session, instance) =>
290 const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
291 var updatedEntities = session.CreateQuery(hqlUpdate)
292 .SetString("path", absolutePath.ToLower())
293 .SetString("checksum", checksum)
295 return updatedEntities;
300 public static void ChangeRootPath(string oldPath, string newPath)
302 if (String.IsNullOrWhiteSpace(oldPath))
303 throw new ArgumentNullException("oldPath");
304 if (!Path.IsPathRooted(oldPath))
305 throw new ArgumentException("oldPath must be an absolute path", "oldPath");
306 if (string.IsNullOrWhiteSpace(newPath))
307 throw new ArgumentNullException("newPath");
308 if (!Path.IsPathRooted(newPath))
309 throw new ArgumentException("newPath must be an absolute path", "newPath");
310 Contract.EndContractBlock();
312 //Ensure the paths end with the same character
313 if (!oldPath.EndsWith("\\"))
314 oldPath = oldPath + "\\";
315 if (!newPath.EndsWith("\\"))
316 newPath = newPath + "\\";
318 ExecuteWithRetry((session, instance) =>
320 const string hqlUpdate =
321 "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
322 var renames = session.CreateQuery(hqlUpdate)
323 .SetString("oldPath", oldPath.ToLower())
324 .SetString("newPath", newPath.ToLower())
330 public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
333 throw new ArgumentOutOfRangeException("blockSize");
334 if (String.IsNullOrWhiteSpace(algorithm))
335 throw new ArgumentNullException("algorithm");
336 Contract.EndContractBlock();
339 var fileState = new FileState
341 FilePath = filePath.ToLower(),
342 OverlayStatus = FileOverlayStatus.Unversioned,
343 FileStatus = FileStatus.Created,
348 return fileState.UpdateHashesAsync(blockSize, algorithm);
351 public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm)
354 throw new ArgumentOutOfRangeException("blockSize");
355 if (String.IsNullOrWhiteSpace(algorithm))
356 throw new ArgumentNullException("algorithm");
357 Contract.EndContractBlock();
359 //Skip updating the hash for folders
360 if (Directory.Exists(FilePath))
363 var hash = await TaskEx.Run(() =>
365 var info = new FileInfo(FilePath);
366 return info.CalculateHash(blockSize, algorithm);
374 private static void ExecuteWithRetry(NHibernateDelegate call, object state)
380 using (new SessionScope())
382 Execute(call, state);
385 catch (ActiveRecordException exc)
391 catch (Exception exc)
399 [ActiveRecord("Tags")]
400 public class FileTag : ActiveRecordLinqBase<FileTag>
403 public int Id { get; set; }
406 public string Name { get; set; }
409 public string Value { get; set; }
411 [BelongsTo("FileStateId")]
412 public FileState FileState { get; set; }