Moved Pithos data and logs from the GRNET folder under AppData to a GRNET\PITHOS...
[pithos-ms-client] / trunk / Pithos.Core / Agents / BlockUpdater.cs
index 6b0d310..3edc216 100644 (file)
@@ -1,4 +1,45 @@
-using System;
+#region
+/* -----------------------------------------------------------------------
+ * <copyright file="BlockUpdater.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;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics.Contracts;
@@ -16,19 +57,38 @@ namespace Pithos.Core.Agents
         public string FilePath { get; private set; }
         public string RelativePath { get; private set; }
 
-        public string FragmentsPath { get; private set; }
+        public string CachePath { get; private set; }
 
         public TreeHash ServerHash { get; private set; }
 
         public string TempPath { get; private set; }
 
+        public bool HasBlocks
+        {
+            get { return _blocks.Count>0; }            
+        }
+
+        readonly ConcurrentDictionary<int, string> _blocks = new ConcurrentDictionary<int, string>();
+        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
+
+        [ContractInvariantMethod]
+        private void Invariants()
+        {
+            Contract.Invariant(Path.IsPathRooted(CachePath));
+            Contract.Invariant(Path.IsPathRooted(FilePath));
+            Contract.Invariant(Path.IsPathRooted(TempPath));
+            Contract.Invariant(!Path.IsPathRooted(RelativePath));
+            Contract.Invariant(_blocks!=null);
+            Contract.Invariant(_orphanBlocks!=null);
+            Contract.Invariant(ServerHash!=null);
+        }
 
-        public BlockUpdater(string fragmentsPath, string filePath, string relativePath,TreeHash serverHash)
+        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)
         {   
-            if (String.IsNullOrWhiteSpace(fragmentsPath))
-                throw new ArgumentNullException("fragmentsPath");
-            if (!Path.IsPathRooted(fragmentsPath))
-                throw new ArgumentException("The fragmentsPath must be rooted", "fragmentsPath");
+            if (String.IsNullOrWhiteSpace(cachePath))
+                throw new ArgumentNullException("cachePath");
+            if (!Path.IsPathRooted(cachePath))
+                throw new ArgumentException("The cachePath must be rooted", "cachePath");
             
             if (string.IsNullOrWhiteSpace(filePath))
                 throw new ArgumentNullException("filePath");
@@ -44,14 +104,21 @@ namespace Pithos.Core.Agents
                 throw new ArgumentNullException("serverHash");
             Contract.EndContractBlock();
 
-            FragmentsPath=fragmentsPath;
+            CachePath=cachePath;
             FilePath = filePath;
             RelativePath=relativePath;
             ServerHash = serverHash;
             //The file will be stored in a temporary location while downloading with an extension .download
-            TempPath = Path.Combine(FragmentsPath, RelativePath + ".download");
-
-            var directoryPath = Path.GetDirectoryName(TempPath);
+            TempPath = Path.Combine(CachePath, RelativePath + ".download");
+            
+            //Need to calculate the directory path because RelativePath may include folders
+            var directoryPath = Path.GetDirectoryName(TempPath);            
+            //directoryPath CAN be null if TempPath is a root path
+            if (String.IsNullOrWhiteSpace(directoryPath))
+                throw new ArgumentException("TempPath");
+            //CachePath was absolute so directoryPath is absolute too
+            Contract.Assume(Path.IsPathRooted(directoryPath));
+            
             if (!Directory.Exists(directoryPath))
                 Directory.CreateDirectory(directoryPath);
 
@@ -64,6 +131,8 @@ namespace Pithos.Core.Agents
                 throw new ArgumentNullException("directoryPath");
             if (!Path.IsPathRooted(directoryPath))
                 throw new ArgumentException("The directoryPath must be rooted", "directoryPath");
+            if (ServerHash==null)
+                throw new InvalidOperationException("ServerHash wasn't initialized");
             Contract.EndContractBlock();
 
             var fileNamename = Path.GetFileName(FilePath);
@@ -76,9 +145,13 @@ namespace Pithos.Core.Agents
                     //The server truncates nulls before calculating hashes, have to do the same
                     //Find the last non-null byte, starting from the end
                     var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);
-                    var binHash = hasher.ComputeHash(buffer,0,lastByteIndex);
-                    var hash = binHash.ToHashString();
-                    _orphanBlocks[hash] = orphan;
+                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file
+                    if (lastByteIndex >= 0)
+                    {
+                        var binHash = hasher.ComputeHash(buffer, 0, lastByteIndex);
+                        var hash = binHash.ToHashString();
+                        _orphanBlocks[hash] = orphan;
+                    }
                 }
             }
         }
@@ -98,7 +171,9 @@ namespace Pithos.Core.Agents
                 File.Copy(FilePath, TempPath, true);
 
             //Set the size of the file to the size specified in the treehash
-            //This will also create an empty file if the file doesn't exist            
+            //This will also create an empty file if the file doesn't exist                        
+            
+            
             SetFileSize(TempPath, ServerHash.Bytes);
 
             //Update the temporary file with the data from the blocks
@@ -132,7 +207,12 @@ namespace Pithos.Core.Agents
             if (File.Exists(FilePath))
                 File.Replace(TempPath, FilePath, null, true);
             else
+            {
+                var targetDirectory = Path.GetDirectoryName(FilePath);
+                if (!Directory.Exists(targetDirectory))
+                    Directory.CreateDirectory(targetDirectory);
                 File.Move(TempPath, FilePath);
+            }
         }
 
         private void ClearBlocks()
@@ -186,8 +266,6 @@ namespace Pithos.Core.Agents
             return (tempLastWrite < localLastWrite);
         }*/
 
-        ConcurrentDictionary<int,string> _blocks=new ConcurrentDictionary<int, string>();
-        ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
 
         public bool UseOrphan(int blockIndex, string blockHash)
         {
@@ -211,21 +289,7 @@ namespace Pithos.Core.Agents
             return FileAsync.WriteAllBytes(blockPath, buffer);
         }
 
-        /*private Task WriteAsync(string filePath, byte[] buffer, int offset, int count)
-        {
-            var stream = FileAsync.OpenWrite(filePath);
-            try
-            {
-                stream.Seek(offset, SeekOrigin.Begin);
-                var write = stream.WriteAsync(buffer, 0, count);
-                return write.ContinueWith(s => stream.Close());
-            }
-            catch (Exception ex)
-            {
-                stream.Close();
-                return Task.Factory.FromException(ex);
-            }
-        }*/
+       
 
     }
 }