#region /* ----------------------------------------------------------------------- * * * Copyright 2011-2012 GRNET S.A. All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and * documentation are those of the authors and should not be * interpreted as representing official policies, either expressed * or implied, of GRNET S.A. * * ----------------------------------------------------------------------- */ #endregion using System.Diagnostics.Contracts; using System.IO; using System.Threading.Tasks; using Castle.ActiveRecord; using Castle.ActiveRecord.Framework; using Pithos.Core.Agents; using Pithos.Interfaces; using Pithos.Network; using log4net; namespace Pithos.Core { using System; using System.Collections.Generic; using System.Linq; /// /// TODO: Update summary. /// [ActiveRecord] public class FileState : ActiveRecordLinqBase { private static readonly ILog Log = LogManager.GetLogger("FileState"); private IList _tags = new List(); [PrimaryKey(PrimaryKeyType.Guid)] public Guid Id { get; set; } [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")] public string FilePath { get; set; } [Property] public FileOverlayStatus OverlayStatus { get; set; } [Property] public FileStatus FileStatus { get; set; } [Property] public string Checksum { get; set; } [Property] public long? Version { get; set; } [Property] public DateTime? VersionTimeStamp { get; set; } [Property] public bool IsShared { get; set; } [Property] public string SharedBy { get; set; } [Property] public bool ShareWrite { get; set; } [Property] public bool IsFolder{ get; set; } [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)] public IList Tags { get { return _tags; } set { _tags = value; } } [Property] public DateTime Modified { get; set; } public FileSystemInfo GetFileSystemInfo() { if (String.IsNullOrWhiteSpace(FilePath)) throw new InvalidOperationException(); Contract.EndContractBlock(); return Directory.Exists(FilePath) ? (FileSystemInfo)new DirectoryInfo(FilePath) : new FileInfo(FilePath); } public string GetRelativeUrl(AccountInfo accountInfo) { if (accountInfo==null) throw new ArgumentNullException("accountInfo"); Contract.EndContractBlock(); var fsi=GetFileSystemInfo(); return fsi.AsRelativeUrlTo(accountInfo.AccountPath); } /*public static FileState FindByFilePath(string absolutePath) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); try { return Queryable.FirstOrDefault(s => s.FilePath == absolutePath); } catch (Exception ex) { Log.Error(ex.ToString()); throw; } }*/ /* public static void DeleteByFilePath(string absolutePath) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlDelete = "delete FileState where FilePath = :path"; var deletedEntities = session.CreateQuery(hqlDelete) .SetString("path", absolutePath) .ExecuteUpdate(); return deletedEntities; }, null); }*/ public static void StoreFileStatus(string absolutePath, FileStatus newStatus) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("path", absolutePath) .SetEnum("status", newStatus) .ExecuteUpdate(); if (updatedEntities == 0) { var newState = new FileState { FilePath = absolutePath, Id = Guid.NewGuid(), FileStatus = newStatus, IsFolder=Directory.Exists(absolutePath) }; newState.CreateAndFlush(); } return null; }, null); } public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set OverlayStatus= :status where FilePath = :path "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("path", absolutePath) .SetEnum("status", newStatus) .ExecuteUpdate(); if (updatedEntities == 0) { var newState = new FileState { FilePath = absolutePath, Id = Guid.NewGuid(), OverlayStatus = newStatus, IsFolder=Directory.Exists(absolutePath) }; newState.CreateAndFlush(); } return null; }, null); } /* public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("path", absolutePath) .SetEnum("fileStatus", fileStatus) .SetEnum("overlayStatus", overlayStatus) .ExecuteUpdate(); return updatedEntities; }, null); } */ /* public static void UpdateStatus(string absolutePath, FileStatus fileStatus) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set FileStatus= :fileStatus where FilePath = :path "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("path", absolutePath) .SetEnum("fileStatus", fileStatus) .ExecuteUpdate(); return updatedEntities; }, null); } */ public static void RenameState(string oldPath, string newPath) { if (string.IsNullOrWhiteSpace(oldPath)) throw new ArgumentNullException("oldPath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set FilePath= :newPath where FilePath = :oldPath "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("oldPath", oldPath) .SetString("newPath", newPath) .ExecuteUpdate(); return updatedEntities; }, null); } /* public static void UpdateStatus(Guid id, FileStatus fileStatus) { ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set FileStatus= :fileStatus where Id = :id "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetGuid("id", id) .SetEnum("fileStatus", fileStatus) .ExecuteUpdate(); return updatedEntities; }, null); }*/ public static void UpdateChecksum(string absolutePath, string checksum) { if (string.IsNullOrWhiteSpace(absolutePath)) throw new ArgumentNullException("absolutePath"); Contract.EndContractBlock(); ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path "; var updatedEntities = session.CreateQuery(hqlUpdate) .SetString("path", absolutePath) .SetString("checksum", checksum) .ExecuteUpdate(); return updatedEntities; }, null); } public static void ChangeRootPath(string oldPath, string newPath) { if (String.IsNullOrWhiteSpace(oldPath)) throw new ArgumentNullException("oldPath"); if (!Path.IsPathRooted(oldPath)) throw new ArgumentException("oldPath must be an absolute path", "oldPath"); if (string.IsNullOrWhiteSpace(newPath)) throw new ArgumentNullException("newPath"); if (!Path.IsPathRooted(newPath)) throw new ArgumentException("newPath must be an absolute path", "newPath"); Contract.EndContractBlock(); //Ensure the paths end with the same character if (!oldPath.EndsWith("\\")) oldPath = oldPath + "\\"; if (!newPath.EndsWith("\\")) newPath = newPath + "\\"; ExecuteWithRetry((session, instance) => { const string hqlUpdate = "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' "; var renames = session.CreateQuery(hqlUpdate) .SetString("oldPath", oldPath) .SetString("newPath", newPath) .ExecuteUpdate(); return renames; }, null); } public static Task CreateForAsync(string filePath, int blockSize, string algorithm) { if (blockSize <= 0) throw new ArgumentOutOfRangeException("blockSize"); if (String.IsNullOrWhiteSpace(algorithm)) throw new ArgumentNullException("algorithm"); Contract.EndContractBlock(); var fileState = new FileState { FilePath = filePath, OverlayStatus = FileOverlayStatus.Unversioned, FileStatus = FileStatus.Created, Id = Guid.NewGuid() }; return fileState.UpdateHashesAsync(blockSize, algorithm); } public async Task UpdateHashesAsync(int blockSize, string algorithm) { if (blockSize <= 0) throw new ArgumentOutOfRangeException("blockSize"); if (String.IsNullOrWhiteSpace(algorithm)) throw new ArgumentNullException("algorithm"); Contract.EndContractBlock(); //Skip updating the hash for folders if (Directory.Exists(FilePath)) return this; var hash = await TaskEx.Run(() => { var info = new FileInfo(FilePath); return info.CalculateHash(blockSize, algorithm); }); Checksum = hash; return this; } private static void ExecuteWithRetry(NHibernateDelegate call, object state) { int retries = 3; while (retries > 0) try { using (new SessionScope()) { Execute(call, state); return; } } catch (ActiveRecordException ) { retries--; if (retries <= 0) throw; } } } [ActiveRecord("Tags")] public class FileTag : ActiveRecordLinqBase { [PrimaryKey] public int Id { get; set; } [Property] public string Name { get; set; } [Property] public string Value { get; set; } [BelongsTo("FileStateId")] public FileState FileState { get; set; } } }