-#region
-/* -----------------------------------------------------------------------
- * <copyright file="FileState.cs" company="GRNet">
- *
- * 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.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Threading.Tasks;
-using Castle.ActiveRecord;
-using Castle.ActiveRecord.Framework;
-using Castle.ActiveRecord.Queries;
-using NHibernate.Criterion;
-using Pithos.Core.Agents;
-using Pithos.Interfaces;
-using Pithos.Network;
-using log4net;
-
-namespace Pithos.Core
-{
- using System;
- using System.Collections.Generic;
-
- /// <summary>
- /// TODO: Update summary.
- /// </summary>
- [ActiveRecord]
- public class FileState : ActiveRecordLinqBase<FileState>
- {
- private static readonly ILog Log = LogManager.GetLogger("FileState");
-
- private IList<FileTag> _tags = new List<FileTag>();
-
- [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<FileTag> 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<FileState> 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<FileState> 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;
- }
- }
-
- /// <summary>
- /// Remove all FileState rows from the database whose path
- /// starts with one of the removed paths
- /// </summary>
- /// <param name="removed"></param>
- public static void RemovePaths(List<string> removed)
- {
- if (removed == null)
- return;
- if (removed.Count == 0)
- return;
-
- //Create a disjunction (list of OR statements
- var disjunction = new Disjunction();
- foreach (var path in removed)
- {
- //with the restriction FileState.FilePath like '@path%'
- disjunction.Add(Restrictions.On<FileState>(s => s.FilePath)
- .IsLike(path, MatchMode.Start));
- }
-
- //Generate a query from the disjunction
- var query=QueryOver.Of<FileState>().Where(disjunction);
- //Adn retrieveonly the IDs
- var idQuery = new ProjectionQuery<FileState,Guid>(query.DetachedCriteria,
- Projections.ProjectionList()
- .Add(Projections.Id()));
-
- var ids=idQuery.Execute();
- DeleteAll(ids);
-
- }
- }
-
- [ActiveRecord("Tags")]
- public class FileTag : ActiveRecordLinqBase<FileTag>
- {
- [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; }
-
- }
-
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="FileState.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer in the documentation and/or other materials\r
+ * provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Reflection;\r
+using System.Threading.Tasks;\r
+using Castle.ActiveRecord;\r
+using Castle.ActiveRecord.Framework;\r
+using Castle.ActiveRecord.Queries;\r
+using NHibernate.Criterion;\r
+using Pithos.Core.Agents;\r
+using Pithos.Interfaces;\r
+using Pithos.Network;\r
+using log4net;\r
+\r
+namespace Pithos.Core\r
+{\r
+ using System;\r
+ using System.Collections.Generic; \r
+\r
+ /// <summary>\r
+ /// TODO: Update summary.\r
+ /// </summary>\r
+ [ActiveRecord]\r
+ public class FileState : ActiveRecordLinqBase<FileState>\r
+ {\r
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+\r
+ private IList<FileTag> _tags = new List<FileTag>();\r
+\r
+ [PrimaryKey(PrimaryKeyType.Guid)]\r
+ public Guid Id { get; set; }\r
+\r
+ \r
+ //[Property(Unique = true, UniqueKey = "IX_FileState_ObjectID")]\r
+ [Property]\r
+ public string ObjectID { get; set; }\r
+\r
+ [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]\r
+ public string FilePath { get; set; }\r
+\r
+ [Property]\r
+ public FileOverlayStatus OverlayStatus { get; set; }\r
+\r
+ [Property]\r
+ public FileStatus FileStatus { get; set; }\r
+\r
+ [Property]\r
+ public string ConflictReason { get; set; }\r
+\r
+ private string _checksum;\r
+\r
+ /// <summary>\r
+ /// The tophash value of the file, calculated by using a Merkle hash with the SHA256 algorithm\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// The SHA256 algorithm is substantially more expenive than other algorithms.\r
+ /// Recalculating the Checksum should be avoided whenever possible.\r
+ /// </remarks>\r
+ [Property]\r
+ public string Checksum\r
+ {\r
+ get\r
+ {\r
+ return _checksum;\r
+ }\r
+ set\r
+ {\r
+ _checksum = value;\r
+ }\r
+ }\r
+ \r
+ /// <summary>\r
+ /// An easy to calcualte hash over the entire file, used to detect file changes\r
+ /// </summary>\r
+ /// <remarks>The algorithm used to calculate this hash should be cheap</remarks>\r
+ [Property(NotNull = true, Default = "")]\r
+ public string ETag { get; set; }\r
+\r
+ [Property(NotNull = true, Default = "")]\r
+ public string LastMD5 { get; set; }\r
+\r
+ [Property]\r
+ public string Hashes { get; set; }\r
+\r
+ [Property]\r
+ public DateTime? LastWriteDate { get; set; }\r
+\r
+ [Property]\r
+ public long? LastLength { get; set; }\r
+ \r
+ [Property]\r
+ public long? Version { get; set; }\r
+\r
+ [Property]\r
+ public DateTime? VersionTimeStamp { get; set; }\r
+\r
+\r
+ [Property]\r
+ public bool IsShared { get; set; }\r
+\r
+ [Property]\r
+ public string SharedBy { get; set; }\r
+\r
+ [Property]\r
+ public bool ShareWrite { get; set; }\r
+\r
+ [Property]\r
+ public bool IsFolder{ get; set; }\r
+\r
+ [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]\r
+ public IList<FileTag> Tags\r
+ {\r
+ get { return _tags; }\r
+ set { _tags = value; }\r
+ }\r
+\r
+ [Property]\r
+ public DateTime Modified { get; set; }\r
+\r
+\r
+ public FileSystemInfo GetFileSystemInfo()\r
+ {\r
+ if (String.IsNullOrWhiteSpace(FilePath))\r
+ throw new InvalidOperationException();\r
+ Contract.EndContractBlock();\r
+\r
+ return Directory.Exists(FilePath) ?\r
+ (FileSystemInfo)new DirectoryInfo(FilePath)\r
+ : new FileInfo(FilePath);\r
+ }\r
+\r
+ public string GetRelativeUrl(AccountInfo accountInfo)\r
+ {\r
+ if (accountInfo==null)\r
+ throw new ArgumentNullException("accountInfo");\r
+ Contract.EndContractBlock();\r
+\r
+ var fsi=GetFileSystemInfo();\r
+ return fsi.AsRelativeUrlTo(accountInfo.AccountPath);\r
+ }\r
+ /*public static FileState FindByFilePath(string absolutePath)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+ try\r
+ {\r
+\r
+ \r
+\r
+ return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ Log.Error(ex.ToString());\r
+ throw;\r
+ }\r
+\r
+\r
+ }*/\r
+\r
+ /* public static void DeleteByFilePath(string absolutePath)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlDelete = "delete FileState where FilePath = :path";\r
+ var deletedEntities = session.CreateQuery(hqlDelete)\r
+ .SetString("path", absolutePath)\r
+ .ExecuteUpdate();\r
+ return deletedEntities;\r
+ }, null);\r
+\r
+ }*/\r
+\r
+/*\r
+ public static void StoreFileStatus(string absolutePath, FileStatus newStatus)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set FileStatus= :status where FilePath = :path ";\r
+ using (session.BeginTransaction())\r
+ {\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetEnum("status", newStatus)\r
+ .ExecuteUpdate();\r
+ if (updatedEntities == 0)\r
+ {\r
+ var newState = new FileState\r
+ {\r
+ FilePath = absolutePath,\r
+ Id = Guid.NewGuid(),\r
+ FileStatus = newStatus,\r
+ IsFolder = Directory.Exists(absolutePath)\r
+ };\r
+ newState.CreateAndFlush();\r
+ }\r
+ return null;\r
+ }\r
+ }, null);\r
+\r
+ }\r
+*/\r
+\r
+ public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus,string etag)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set OverlayStatus= :status where FilePath = :path ";\r
+ using (var tx=session.BeginTransaction())\r
+ {\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetEnum("status", newStatus)\r
+ .ExecuteUpdate();\r
+ if (updatedEntities == 0)\r
+ {\r
+ var newState = new FileState\r
+ {\r
+ FilePath = absolutePath,\r
+ Id = Guid.NewGuid(),\r
+ OverlayStatus = newStatus,\r
+ ETag = etag ?? String.Empty,\r
+ LastMD5=String.Empty,\r
+ IsFolder = Directory.Exists(absolutePath)\r
+ };\r
+ newState.CreateAndFlush();\r
+ }\r
+ tx.Commit();\r
+ return null;\r
+ }\r
+ }, null);\r
+\r
+ }\r
+\r
+/*\r
+ public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetEnum("fileStatus", fileStatus)\r
+ .SetEnum("overlayStatus", overlayStatus)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+\r
+ }\r
+*/\r
+\r
+/*\r
+ public static void UpdateStatus(string absolutePath, FileStatus fileStatus)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set FileStatus= :fileStatus where FilePath = :path ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetEnum("fileStatus", fileStatus)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+\r
+ }\r
+\r
+*/\r
+ public static void RenameState(string oldPath, string newPath)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(oldPath))\r
+ throw new ArgumentNullException("oldPath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set FilePath= :newPath where FilePath = :oldPath ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("oldPath", oldPath)\r
+ .SetString("newPath", newPath)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+\r
+ }\r
+\r
+ /* public static void UpdateStatus(Guid id, FileStatus fileStatus)\r
+ {\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set FileStatus= :fileStatus where Id = :id ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetGuid("id", id)\r
+ .SetEnum("fileStatus", fileStatus)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+ }*/\r
+\r
+ public static void UpdateFileTreeHash(string absolutePath, TreeHash treeHash)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ \r
+ var hashes=treeHash.ToJson();\r
+ const string hqlUpdate = "update FileState set Checksum= :checksum,ETag=:etag, Hashes=:hashes where FilePath = :path ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetString("checksum", treeHash.TopHash.ToHashString())\r
+ .SetString("etag", treeHash.MD5)\r
+ .SetString("hashes",hashes)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+ }\r
+\r
+ public static void UpdateChecksum(string absolutePath, string etag, string checksum)\r
+ {\r
+ if (string.IsNullOrWhiteSpace(absolutePath))\r
+ throw new ArgumentNullException("absolutePath");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate = "update FileState set Checksum= :checksum,ETag=:etag where FilePath = :path ";\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetString("path", absolutePath)\r
+ .SetString("checksum", checksum)\r
+ .SetString("etag", etag)\r
+ .ExecuteUpdate();\r
+ return updatedEntities;\r
+ }, null);\r
+\r
+ }\r
+\r
+\r
+ public static void UpdateLastMD5(FileInfo file, string md5)\r
+ {\r
+ if (file==null)\r
+ throw new ArgumentNullException("file");\r
+ Contract.EndContractBlock();\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate = "update FileState set LastMD5=:md5, LastWriteDate=:date,LastLength=:length where FilePath = :path ";\r
+ \r
+ file.Refresh();\r
+ if (!file.Exists)\r
+ return 0;\r
+ var fullName = file.WithProperCapitalization().FullName;\r
+\r
+ using (var tx=session.BeginTransaction())\r
+ {\r
+ var updatedEntities = session.CreateQuery(hqlUpdate)\r
+ .SetDateTime("date", file.LastWriteTime)\r
+ .SetInt64("length", file.Length)\r
+ .SetString("md5", md5)\r
+ .SetString("path", fullName)\r
+ .ExecuteUpdate();\r
+ if (updatedEntities == 0)\r
+ {\r
+ var newState = new FileState\r
+ {\r
+ FilePath = fullName,\r
+ Id = Guid.NewGuid(),\r
+ OverlayStatus = FileOverlayStatus.Normal,\r
+ FileStatus = FileStatus.Unchanged,\r
+ IsFolder = false,\r
+ LastLength = file.Length,\r
+ LastWriteDate = file.LastWriteTime,\r
+ LastMD5 = md5 ?? String.Empty,\r
+ ETag = String.Empty\r
+ };\r
+ newState.CreateAndFlush();\r
+ }\r
+ tx.Commit();\r
+ return updatedEntities;\r
+ }\r
+ }, null);\r
+\r
+ }\r
+\r
+ public static void ChangeRootPath(string oldPath, string newPath)\r
+ {\r
+ if (String.IsNullOrWhiteSpace(oldPath))\r
+ throw new ArgumentNullException("oldPath");\r
+ if (!Path.IsPathRooted(oldPath))\r
+ throw new ArgumentException("oldPath must be an absolute path", "oldPath");\r
+ if (string.IsNullOrWhiteSpace(newPath))\r
+ throw new ArgumentNullException("newPath");\r
+ if (!Path.IsPathRooted(newPath))\r
+ throw new ArgumentException("newPath must be an absolute path", "newPath");\r
+ Contract.EndContractBlock();\r
+\r
+ //Ensure the paths end with the same character\r
+ if (!oldPath.EndsWith("\\"))\r
+ oldPath = oldPath + "\\";\r
+ if (!newPath.EndsWith("\\"))\r
+ newPath = newPath + "\\";\r
+\r
+ ExecuteWithRetry((session, instance) =>\r
+ {\r
+ const string hqlUpdate =\r
+ "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";\r
+ var renames = session.CreateQuery(hqlUpdate)\r
+ .SetString("oldPath", oldPath)\r
+ .SetString("newPath", newPath)\r
+ .ExecuteUpdate();\r
+ return renames;\r
+ }, null);\r
+ }\r
+\r
+ public static FileState CreateFor(FileSystemInfo info,IStatusNotification notification)\r
+ {\r
+ if(info==null)\r
+ throw new ArgumentNullException("info");\r
+ Contract.EndContractBlock();\r
+ \r
+ if (info is DirectoryInfo)\r
+ return new FileState\r
+ {\r
+ FilePath = info.FullName,\r
+ OverlayStatus = FileOverlayStatus.Unversioned,\r
+ FileStatus = FileStatus.Created,\r
+ ETag=String.Empty,\r
+ LastMD5=String.Empty,\r
+ Id = Guid.NewGuid()\r
+ };\r
+\r
+ \r
+ var etag = ((FileInfo)info).ComputeShortHash(notification);\r
+ var fileState = new FileState\r
+ {\r
+ FilePath = info.FullName,\r
+ OverlayStatus = FileOverlayStatus.Unversioned,\r
+ FileStatus = FileStatus.Created, \r
+ ETag=etag,\r
+ LastMD5=String.Empty,\r
+ Id = Guid.NewGuid()\r
+ };\r
+ return fileState;\r
+ }\r
+\r
+\r
+ private static void ExecuteWithRetry(NHibernateDelegate call, object state)\r
+ {\r
+ int retries = 3;\r
+ while (retries > 0)\r
+ try\r
+ {\r
+ using (new SessionScope())\r
+ {\r
+ Execute(call, state);\r
+ return;\r
+ }\r
+ }\r
+ catch (ActiveRecordException )\r
+ {\r
+ retries--;\r
+ if (retries <= 0)\r
+ throw;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Mark Unversioned all FileState rows from the database whose path\r
+ /// starts with one of the removed paths\r
+ /// </summary>\r
+ /// <param name="removed"></param>\r
+ public static void UnversionPaths(List<string> removed)\r
+ {\r
+ if (removed == null)\r
+ return;\r
+ if (removed.Count == 0)\r
+ return;\r
+\r
+ //Create a disjunction (list of OR statements\r
+ var disjunction = new Disjunction(); \r
+ foreach (var path in removed)\r
+ {\r
+ //with the restriction FileState.FilePath like '@path%'\r
+ disjunction.Add(Restrictions.On<FileState>(s => s.FilePath)\r
+ .IsLike(path, MatchMode.Start));\r
+ }\r
+\r
+ //Generate a query from the disjunction\r
+ var query=QueryOver.Of<FileState>().Where(disjunction);\r
+ \r
+ ExecuteWithRetry((session,instance)=>\r
+ {\r
+ using (var tx=session.BeginTransaction())\r
+ {\r
+ var states = query.GetExecutableQueryOver(session).List();\r
+ foreach (var state in states)\r
+ {\r
+ state.FileStatus = FileStatus.Unversioned;\r
+ state.OverlayStatus = FileOverlayStatus.Unversioned;\r
+ state.Update();\r
+ }\r
+ tx.Commit();\r
+ }\r
+ return null;\r
+ },null);\r
+ }\r
+\r
+ public string ToDebugString()\r
+ {\r
+ return String.Format("[STATE] FilePath:[{0}] ObjectID:[{1}], ETAG: [{2}], LastMD5:[{3}]", FilePath, ObjectID,\r
+ ETag, LastMD5);\r
+ }\r
+ }\r
+\r
+ [ActiveRecord("Tags")]\r
+ public class FileTag : ActiveRecordLinqBase<FileTag>\r
+ {\r
+ [PrimaryKey]\r
+ public int Id { get; set; }\r
+\r
+ [Property]\r
+ public string Name { get; set; }\r
+\r
+ [Property]\r
+ public string Value { get; set; }\r
+\r
+ [BelongsTo("FileStateId")]\r
+ public FileState FileState { get; set; }\r
+\r
+ }\r
+ \r
+}\r