Revision 5ce54458

b/trunk/Pithos.Client.WPF/Pithos.Client.WPF.csproj
117 117
      <DependentUpon>FilePropertiesView.xaml</DependentUpon>
118 118
    </Compile>
119 119
    <Compile Include="FilePropertiesViewModel.cs" />
120
    <Compile Include="NativeMethods.cs" />
120 121
    <Compile Include="Notification.cs" />
121 122
    <Compile Include="PithosAccount.cs" />
122 123
    <Compile Include="PithosCommand.cs" />
b/trunk/Pithos.Client.WPF/PreferencesView.xaml
16 16
            </ResourceDictionary.MergedDictionaries>
17 17
        </ResourceDictionary>
18 18
    </Window.Resources>
19
    <Window.TaskbarItemInfo>
19
<!--    <Window.TaskbarItemInfo>
20 20
        <TaskbarItemInfo Description="{Binding StatusMessage}" >
21 21
            <TaskbarItemInfo.ThumbButtonInfos>
22 22
                <ThumbButtonInfo>
......
24 24
                </ThumbButtonInfo>
25 25
            </TaskbarItemInfo.ThumbButtonInfos>
26 26
        </TaskbarItemInfo>
27
    </Window.TaskbarItemInfo>
27
    </Window.TaskbarItemInfo>-->
28 28
    <Grid>
29 29
        <Grid.RowDefinitions>
30 30
            <RowDefinition Height="*"/>
b/trunk/Pithos.Client.WPF/PreferencesViewModel.cs
12 12
using System.Linq.Expressions;
13 13
using System.Net;
14 14
using System.Reflection;
15
using System.Runtime.InteropServices;
15 16
using System.Runtime.Serialization;
16 17
using System.Windows;
17 18
using System.Windows.Forms;
......
45 46

  
46 47
        public PithosMonitor Monitor { get; private set; }
47 48

  
48
        public TaskbarViewModel Taskbar { get; set; }
49
        private TaskbarViewModel _taskbar;
50
        public TaskbarViewModel Taskbar
51
        {
52
            get { return _taskbar; }
53
            set
54
            {
55
                _taskbar = value;
56
            }
57
        }
49 58

  
50 59
        //ShellExtensionController _extensionController=new ShellExtensionController();
51 60

  
......
257 266

  
258 267
        public void RefreshOverlays()
259 268
        {
260
            this.Monitor.Workflow.RaiseChangeNotification(Settings.PithosPath);
269
            string path=Settings.PithosPath;
270
            if (String.IsNullOrWhiteSpace(path))
271
                throw new ArgumentNullException("path", "The path parameter must not be emtpy");
272

  
273
            if (!Directory.Exists(path) && !File.Exists(path))
274
                throw new FileNotFoundException("The specified file or path does not exist", path);
275

  
276

  
277
            IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
278

  
279
            try
280
            {
281
                NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
282
                                             HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
283
                                             pathPointer, IntPtr.Zero);
284
            }
285
            finally
286
            {
287
                Marshal.FreeHGlobal(pathPointer);
288
            }
289

  
261 290
        }
262 291
        #endregion
263 292

  
b/trunk/Pithos.Client.WPF/Properties/Settings.Designer.cs
242 242
</ArrayOfAccountSettings>")]
243 243
        public global::Pithos.Interfaces.AccountsCollection Accounts {
244 244
            get {
245
                return ((global::Pithos.Interfaces.AccountsCollection)(this["Accounts"]));
245
                    return ((global::Pithos.Interfaces.AccountsCollection)(this["Accounts"]));
246 246
            }
247 247
            set {
248 248
                this["Accounts"] = value;
b/trunk/Pithos.Client.WPF/TaskbarViewModel.cs
105 105
        }
106 106

  
107 107

  
108
        private string _statusIcon;
108
        private string _statusIcon="Images/Tray.ico";
109 109
        public string StatusIcon
110 110
        {
111 111
            get { return _statusIcon; }
b/trunk/Pithos.Core.Test/MockStatusKeeper.cs
6 6
using System.Text;
7 7
using System.Threading;
8 8
using Pithos.Interfaces;
9
using Pithos.Network;
9 10

  
10 11
namespace Pithos.Core.Test
11 12
{
......
95 96
            throw new NotImplementedException();
96 97
        }
97 98

  
98
        ConcurrentDictionary<string, NetworkState> _networkState = new ConcurrentDictionary<string, NetworkState>();
99
        ConcurrentDictionary<string, NetworkOperation> _networkState = new ConcurrentDictionary<string, NetworkOperation>();
99 100

  
100 101

  
101
        public void SetNetworkState(string path, NetworkState state)
102
        public void SetNetworkState(string path, NetworkOperation operation)
102 103
        {
103
            _networkState[path.ToLower()] = state;
104
            _networkState[path.ToLower()] = operation;
104 105
            //Removing may fail so we store the "None" value anyway
105
            if (state == NetworkState.None)
106
            if (operation == NetworkOperation.None)
106 107
            {
107
                NetworkState oldState;
108
                _networkState.TryRemove(path, out oldState);
108
                NetworkOperation oldOperation;
109
                _networkState.TryRemove(path, out oldOperation);
109 110
            }
110 111
        }
111 112

  
112
        public NetworkState GetNetworkState(string path)
113
        public NetworkOperation GetNetworkState(string path)
113 114
        {
114
            NetworkState state;
115
            if (_networkState.TryGetValue(path, out state))
116
                return state;
117
            return NetworkState.None;
115
            NetworkOperation operation;
116
            if (_networkState.TryGetValue(path, out operation))
117
                return operation;
118
            return NetworkOperation.None;
118 119
        }
119 120

  
120 121
        public void StartProcessing(CancellationToken token)
......
124 125

  
125 126
        public string BlockHash { get; set; }
126 127

  
128
        public int BlockSize { get; set; }
129

  
127 130

  
128 131
        private PithosStatus _pithosStatus = PithosStatus.InSynch;
129 132
        public void SetPithosStatus(PithosStatus status)
b/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj
92 92
  <ItemGroup>
93 93
    <Compile Include="PithosWorkflowTest.cs" />
94 94
    <Compile Include="Properties\AssemblyInfo.cs" />
95
    <Compile Include="SignatureTest.cs" />
96 95
    <Compile Include="StatusCheckerTest.cs" />
97 96
    <Compile Include="MockSettings.cs" />
98 97
    <Compile Include="MockStatusKeeper.cs" />
......
111 110
      <Project>{7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}</Project>
112 111
      <Name>Pithos.Interfaces</Name>
113 112
    </ProjectReference>
113
    <ProjectReference Include="..\Pithos.Network\Pithos.Network.csproj">
114
      <Project>{C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}</Project>
115
      <Name>Pithos.Network</Name>
116
    </ProjectReference>
114 117
  </ItemGroup>
115 118
  <ItemGroup>
116 119
    <None Include="packages.config" />
b/trunk/Pithos.Core.Test/PithosWorkflowTest.cs
33 33

  
34 34
            Assert.DoesNotThrow(() =>
35 35
                                    {
36
                                        workflow.RaiseChangeNotification(path);
36
                                        NativeMethods.RaiseChangeNotification(path);
37 37
                                    });
38 38
            path = @"e:\pithos\01New folder";
39
            Assert.DoesNotThrow(() =>
40
                                    {
41
                                        workflow.RaiseChangeNotification(path);
42
                                    });
39
            Assert.DoesNotThrow(() => NativeMethods.RaiseChangeNotification(path));
43 40

  
44 41
        }
45 42

  
b/trunk/Pithos.Core/Agents/NetworkAgent.cs
6 6
using System.IO;
7 7
using System.Linq;
8 8
using System.Text;
9
using System.Threading;
10 9
using System.Threading.Tasks;
11 10
using Pithos.Interfaces;
11
using Pithos.Network;
12 12

  
13 13
namespace Pithos.Core.Agents
14 14
{
......
23 23
        public IStatusNotification StatusNotification { get; set; }
24 24
        [Import]
25 25
        public ICloudClient CloudClient { get; set; }
26

  
27
        [Import]
28
        public FileAgent FileAgent {get;set;}
29

  
30
        /*
26 31
        [Import]
27 32
        public IPithosWorkflow Workflow { get; set; }
33
*/
34

  
28 35

  
29
        private string _rootPath;
30 36
        private string _pithosContainer;
31 37
        private string _trashContainer;
32 38

  
39
        private int _blockSize;
40
        private string _blockHash;
33 41

  
34
        public void Start(string rootPath,string pithosContainer,string trashContainer)
42

  
43
        public void Start(string pithosContainer, string trashContainer, int blockSize, string blockHash)
35 44
        {
36
            if (String.IsNullOrWhiteSpace(rootPath))
37
                throw new ArgumentNullException("rootPath");
38 45
            if (String.IsNullOrWhiteSpace(pithosContainer))
39 46
                throw new ArgumentNullException("pithosContainer");
40 47
            if (String.IsNullOrWhiteSpace(trashContainer))
41 48
                throw new ArgumentNullException("trashContainer");
42 49
            Contract.EndContractBlock();
43 50

  
44
            _rootPath = rootPath;
45 51
            _pithosContainer = pithosContainer;
46 52
            _trashContainer = trashContainer;
53
            _blockSize = blockSize;
54
            _blockHash = blockHash;
55

  
47 56

  
48 57
            _agent = Agent<CloudAction>.Start(inbox =>
49 58
            {
......
54 63
                    var process = message.ContinueWith(t =>
55 64
                    {
56 65
                        var action = t.Result;
66
                        
57 67
                        Process(action);
58 68
                        inbox.DoAsync(loop);
59 69
                    });
......
73 83
            });
74 84
        }
75 85

  
86

  
76 87
        public void Post(CloudAction cloudAction)
77 88
        {
78 89
            if (cloudAction == null)
......
82 93
            _agent.Post(cloudAction);
83 94
        }
84 95

  
96
        class ObjectInfoByNameComparer:IEqualityComparer<ObjectInfo>
97
        {
98
            public bool Equals(ObjectInfo x, ObjectInfo y)
99
            {
100
                return x.Name.Equals(y.Name,StringComparison.InvariantCultureIgnoreCase);
101
            }
102

  
103
            public int GetHashCode(ObjectInfo obj)
104
            {
105
                return obj.Name.ToLower().GetHashCode();
106
            }
107
        }
108

  
85 109
        public Task ProcessRemoteFiles(string accountPath,DateTime? since=null)
86 110
        {            
87 111

  
......
94 118

  
95 119
            var enqueueFiles = listObjects.ContinueWith(task =>
96 120
            {
121
                //ListObjects failed at this point, need to reschedule
122
                if (task.IsFaulted)
123
                {
124
                    Trace.TraceError("[FAIL] ListObjects in ProcessRemoteFiles with {0}",task.Exception);
125
                    ProcessRemoteFiles(accountPath, since);
126
                    return;
127
                }
97 128
                Trace.CorrelationManager.StartLogicalOperation("Listener");
98 129
                Trace.TraceInformation("[LISTENER] Start Processing");
99 130

  
100 131
                var remoteObjects = task.Result;
101 132

  
102
                var pithosDir = new DirectoryInfo(accountPath);
133
                var remote=(from info in remoteObjects
134
                                  let name=info.Name
135
                                  where !name.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase) &&
136
                                  !name.StartsWith("fragments/",StringComparison.InvariantCultureIgnoreCase)
137
                                select info)
138
                                .Distinct(new ObjectInfoByNameComparer()).ToDictionary(info=> info.Name.ToLower(), info=>info);
139

  
140
                var commonObjects = new List<Tuple<ObjectInfo, FileInfo>>();
141
                var localFiles = new List<FileInfo>();
142
                
143
                //In order to avoid multiple iterations over the files, we iterate only once
144
                foreach (var fileInfo in FileAgent.EnumerateFileInfos())
145
                {
146
                    var relativeUrl=fileInfo.AsRelativeUrlTo(FileAgent.RootPath);
147
                    //and remove any matching objects from the list, adding them to the commonObjects list
148
                    if (remote.ContainsKey(relativeUrl))
149
                    {
150
                        commonObjects.Add(Tuple.Create(remote[relativeUrl], fileInfo));
151
                        remote.Remove(relativeUrl);
152
                    }
153
                    else
154
                        //If there is no match we add them to the localFiles list
155
                        localFiles.Add(fileInfo);
156
                }
103 157

  
104
                var remoteFiles = from info in remoteObjects
105
                                  select info.Name.ToLower();
158
                //At the end of the iteration, the *remote* list will contain the files that exist 
159
                //only on the server
106 160

  
107
                var onlyLocal = from localFile in pithosDir.EnumerateFiles()
108
                                where !remoteFiles.Contains(localFile.Name.ToLower())
109
                                select new CloudAction(CloudActionType.UploadUnconditional, localFile, ObjectInfo.Empty);
161
                //Local files should be uploaded
162
                var actionsForLocal = from localFile in localFiles
163
                                select new CloudAction(CloudActionType.UploadUnconditional, 
164
                                    localFile, 
165
                                    ObjectInfo.Empty);                
110 166

  
111
                var localNames = from info in pithosDir.EnumerateFiles()
112
                                 select info.Name.ToLower();
167
                //Remote files should be downloaded
168
                var actionsForRemote = from dictPair in remote
169
                                       let upFile = dictPair.Value
170
                                       select new CloudAction(CloudActionType.DownloadUnconditional, null, upFile);
113 171

  
114
                var onlyRemote = from upFile in remoteObjects
115
                                 where !localNames.Contains(upFile.Name.ToLower())
116
                                 select new CloudAction(CloudActionType.DownloadUnconditional, null, upFile);
172
                //Common files should be checked on a per-case basis to detect differences, which is newer
173
                var actionsForCommon = from pair in commonObjects
174
                                       let objectInfo = pair.Item1
175
                                       let localFile = pair.Item2
176
                                       select new CloudAction(CloudActionType.MustSynch, localFile, objectInfo);
117 177

  
118
                
119
                var commonObjects = from upFile in remoteObjects
120
                                    join localFile in pithosDir.EnumerateFiles()
121
                                        on upFile.Name.ToLower() equals localFile.Name.ToLower()
122
                                    select new CloudAction(CloudActionType.MustSynch, localFile, upFile);
178
                //Collect all the actions
179
                var allActions = actionsForLocal.Union(actionsForRemote).Union(actionsForCommon);
123 180

  
124
                var uniques =
125
                    onlyLocal.Union(onlyRemote).Union(commonObjects)
181
                //And remove those that are already being processed by the agent
182
                var distinctActions =allActions
126 183
                        .Except(_agent.GetEnumerable(), new PithosMonitor.LocalFileComparer())
127 184
                        .ToList();
128 185

  
129
                _agent.AddFromEnumerable(uniques);
186
                //Queue all the actions
187
                _agent.AddFromEnumerable(distinctActions);
130 188

  
131
                StatusNotification.NotifyChange(String.Format("Processing {0} files", uniques.Count));
189
                StatusNotification.NotifyChange(String.Format("Processing {0} files", distinctActions.Count));
132 190

  
133 191
                Trace.TraceInformation("[LISTENER] End Processing");
134 192
                Trace.CorrelationManager.StopLogicalOperation();
......
145 203
                {
146 204
                    Trace.TraceInformation("[LISTENER] Finished");
147 205
                }
148
                ProcessRemoteFiles(accountPath,nextSince);
206
                ProcessRemoteFiles(accountPath, nextSince);
207
                
149 208
            });
150 209
            return loop;
151 210
        }
152 211

  
212
        
153 213

  
154 214

  
155 215
        private void Process(CloudAction action)
......
162 222
            var localFile = action.LocalFile;
163 223
            var cloudFile = action.CloudFile;
164 224
            var downloadPath = (cloudFile == null) ? String.Empty
165
                                    : Path.Combine(_rootPath, cloudFile.Name);
225
                                    : Path.Combine(FileAgent.RootPath, cloudFile.Name.RelativeUrlToFilePath());
226
            
166 227
            try
167 228
            {
168 229
                switch (action.Action)
169 230
                {
170 231
                    case CloudActionType.UploadUnconditional:
171
                        UploadCloudFile(localFile.Name, localFile.FullName, action.LocalHash.Value);
232
                        UploadCloudFile(localFile, action.LocalHash.Value);
172 233
                        break;
173 234
                    case CloudActionType.DownloadUnconditional:
174
                        DownloadCloudFile(_pithosContainer, cloudFile.Name, downloadPath);
235
                        DownloadCloudFile(_pithosContainer, new Uri(cloudFile.Name,UriKind.Relative), downloadPath);
175 236
                        break;
176 237
                    case CloudActionType.DeleteCloud:
177 238
                        DeleteCloudFile(cloudFile.Name);
......
194 255
                                    //Maybe need to store version as well, to check who has the latest version
195 256

  
196 257
                                    //StatusKeeper.SetFileOverlayStatus(downloadPath, FileOverlayStatus.Conflict);
197
                                    UploadCloudFile(localFile.Name, localFile.FullName, action.LocalHash.Value);
258
                                    UploadCloudFile(localFile, action.LocalHash.Value);
198 259
                                }
199 260
                                else
200 261
                                {
......
204 265
                                        case FileStatus.Unchanged:
205 266
                                            //It he cloud file has a later date, it was modified by another user or computer.
206 267
                                            //If the local file's status is Unchanged, we should go on and download the cloud file
207
                                            DownloadCloudFile(_pithosContainer, action.CloudFile.Name, downloadPath);
268
                                            DownloadCloudFile(_pithosContainer, new Uri(action.CloudFile.Name,UriKind.Relative), downloadPath);
208 269
                                            break;
209 270
                                        case FileStatus.Modified:
210 271
                                            //If the local file is Modified, we may have a conflict. In this case we should mark the file as Conflict
......
230 291
                            }
231 292
                        }
232 293
                        else
233
                            DownloadCloudFile(_pithosContainer, action.CloudFile.Name, downloadPath);
294
                            DownloadCloudFile(_pithosContainer, new Uri(action.CloudFile.Name,UriKind.Relative), downloadPath);
234 295
                        break;
235 296
                }
236 297
                Trace.TraceInformation("[ACTION] End Processing {0}:{1}->{2}", action.Action, action.LocalFile, action.CloudFile.Name);
......
267 328

  
268 329
            this.StatusKeeper.SetFileStatus(newPath, FileStatus.Unchanged);
269 330
            this.StatusKeeper.SetFileOverlayStatus(newPath, FileOverlayStatus.Normal);
270
            Workflow.RaiseChangeNotification(newPath);
331
            NativeMethods.RaiseChangeNotification(newPath);
271 332
        }
272 333

  
273 334
        private void DeleteCloudFile(string fileName)
......
285 346
            this.StatusKeeper.RemoveFileOverlayStatus(fileName);
286 347
        }
287 348

  
288
        private void DownloadCloudFile(string container, string fileName, string localPath)
349
        //Download a file.
350
        private void DownloadCloudFile(string container, Uri relativeUrl, string localPath)
289 351
        {
290 352
            if (String.IsNullOrWhiteSpace(container))
291 353
                throw new ArgumentNullException("container");
292
            if (String.IsNullOrWhiteSpace(fileName))
293
                throw new ArgumentNullException("fileName");
354
            if (relativeUrl==null)
355
                throw new ArgumentNullException("relativeUrl");
356

  
294 357
            if (String.IsNullOrWhiteSpace(localPath))
295 358
                throw new ArgumentNullException("localPath");
296 359
            if (!Path.IsPathRooted(localPath))
297 360
                throw new ArgumentException("The localPath must be rooted", "localPath");
298 361
            Contract.EndContractBlock();
299 362

  
300
            var state = StatusKeeper.GetNetworkState(localPath);
301
            //Abort if the file is already being uploaded or downloaded
302
            if (state != NetworkState.None)
363
            var url = relativeUrl.ToString();
364
            if (url.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase))
303 365
                return;
304 366

  
305
            StatusKeeper.SetNetworkState(localPath, NetworkState.Downloading);
306

  
307
            var tempPath = Path.GetTempFileName();
367
            //Are we already downloading or uploading the file? 
368
            using (var gate=NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
369
            {
370
                if (gate.Failed)
371
                    return;
372
                //Calculate the relative file path for the new file
373
                var relativePath = relativeUrl.RelativeUriToFilePath();
374
                //The file will be stored in a temporary location while downloading with an extension .download
375
                var tempPath = Path.Combine(FileAgent.FragmentsPath, relativePath + ".download");
376
                //The file's hashmap will be stored in the same location with the extension .hashmap
377
                var hashPath = Path.Combine(FileAgent.FragmentsPath, relativePath + ".hashmap");
378
                
379
                //Retrieve the hashmap from the server
380
                var getHashMap = CloudClient.GetHashMap(container,url);
381
                //And save it
382
                var saveHashMap = getHashMap.ContinueWith(t =>
383
                {
384
                    var treeHash = t.Result;
385
                    treeHash.Save(hashPath);
386
                });
308 387

  
309
            var getObject = CloudClient.GetObject(container, fileName, tempPath).ContinueWith(t =>
310
                            File.Move(tempPath, localPath));
388
                //Make sure the target folder exists. DownloadFileTask will not create the folder
389
                var directoryPath=Path.GetDirectoryName(tempPath);
390
                if (!Directory.Exists(directoryPath))
391
                    Directory.CreateDirectory(directoryPath);
311 392

  
312
            var getInfo = getObject.ContinueWith(t =>
313
                            CloudClient.GetObjectInfo(container, fileName));
314
            var storeInfo = getInfo.ContinueWith(t =>
315
                            StatusKeeper.StoreInfo(fileName, t.Result));
393
                //Download the object to the temporary location
394
                var getObject = CloudClient.GetObject(container, url, tempPath).ContinueWith(t =>
395
                {
396
                    //And move it to its actual location once downloading is finished
397
                    if (File.Exists(localPath))
398
                        File.Replace(tempPath,localPath,null,true);
399
                    else
400
                        File.Move(tempPath,localPath);
401
                });
402
                
403
                //Retrieve the object's metadata
404
                var getInfo = saveHashMap.ContinueWith(t => getObject).ContinueWith(t =>
405
                            CloudClient.GetObjectInfo(container, url));
406
                //And store it
407
                var storeInfo = getInfo.ContinueWith(t =>
408
                            StatusKeeper.StoreInfo(localPath, t.Result));
316 409

  
317
            storeInfo.Wait();
410
                storeInfo.Wait();
318 411

  
319
            StatusKeeper.SetNetworkState(localPath, NetworkState.None);
412
            }
320 413
        }
321 414

  
322
        private void UploadCloudFile(string fileName, string path, string hash)
415
        private void UploadCloudFile(FileInfo fileInfo, string hash)
323 416
        {
324
            if (String.IsNullOrWhiteSpace(fileName))
325
                throw new ArgumentNullException("fileName");
326
            if (Path.IsPathRooted(fileName))
327
                throw new ArgumentException("The fileName should not be rooted", "fileName");
328
            if (String.IsNullOrWhiteSpace(path))
329
                throw new ArgumentNullException("path");
417
            if (fileInfo==null)
418
                throw new ArgumentNullException("fileInfo");
330 419
            if (String.IsNullOrWhiteSpace(hash))
331 420
                throw new ArgumentNullException("hash");
332 421
            Contract.EndContractBlock();
333 422

  
334
            var state = StatusKeeper.GetNetworkState(path);
335
            //Abort if the file is already being uploaded or downloaded
336
            if (state != NetworkState.None)
423
            if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
337 424
                return;
425
            
426
            var url = fileInfo.AsRelativeUrlTo(FileAgent.RootPath);
338 427

  
339
            StatusKeeper.SetNetworkState(path, NetworkState.Uploading);
428
            using(var gate=NetworkGate.Acquire(fileInfo.FullName,NetworkOperation.Uploading))
429
            {
430
                //Abort if the file is already being uploaded or downloaded
431
                if (gate.Failed)
432
                    return;
340 433

  
341
            //Even if GetObjectInfo times out, we can proceed with the upload            
342
            var info = CloudClient.GetObjectInfo(_pithosContainer, fileName);
343 434

  
344
            var putOrUpdate = Task.Factory.StartNew(() =>
345
            {
346
                if (!hash.Equals(info.Hash, StringComparison.InvariantCultureIgnoreCase))
435
                //Even if GetObjectInfo times out, we can proceed with the upload            
436
                var info = CloudClient.GetObjectInfo(_pithosContainer, url);
437

  
438
                //If the file hashes match, abort the upload
439
                if (hash.Equals(info.Hash, StringComparison.InvariantCultureIgnoreCase))
347 440
                {
348
                    var setStatus = Task.Factory.StartNew(() =>
349
                            StatusKeeper.SetFileOverlayStatus(path, FileOverlayStatus.Modified));
350
                    var put = setStatus.ContinueWith(t =>
351
                            CloudClient.PutObject(_pithosContainer, fileName, path, hash));
441
                    //but store any metadata changes 
442
                    this.StatusKeeper.StoreInfo(fileInfo.FullName, info);
443
                    Trace.TraceInformation("Skip upload of {0}, hashes match", fileInfo.FullName);
444
                    return;
352 445
                }
353
                else
446

  
447
                //If the file was last modified by a hashmap operation, its hash will be a treehash
448
                //We must load the local file's treehash and check it against the object's hash 
449
                //The hash will be stored under the fragments path, in the same relative path as to
450
                //the pithos root path
451
                var relativePath = fileInfo.AsRelativeTo(FileAgent.RootPath);
452
                var hashPath = Path.Combine(FileAgent.FragmentsPath, relativePath) + ".hashmap";                
453
                //Load the hash or calculate a new one
454
                var hashFileExists = File.Exists(hashPath);
455
                var treeHash = hashFileExists ?
456
                    TreeHash.LoadTreeHash(hashPath, Guid.NewGuid()).Result
457
                    : Signature.CalculateTreeHash(fileInfo.FullName, _blockSize, _blockHash);
458
                //If this is a new treehash, store it
459
                if (!hashFileExists)
460
                    treeHash.Save(hashPath);
461

  
462
                var topHash = treeHash.TopHash.ToHashString();
463
                if (topHash.Equals(info.Hash, StringComparison.InvariantCultureIgnoreCase))
354 464
                {
355
                    this.StatusKeeper.StoreInfo(path, info);
465
                    this.StatusKeeper.StoreInfo(fileInfo.FullName, info);
466
                    Trace.TraceInformation("Skip upload of {0}, treehashes match", fileInfo.FullName);
467
                    return;                    
356 468
                }
357
            });
358
            putOrUpdate.Wait();
469
            
359 470

  
360
            this.StatusKeeper.SetFileState(path, FileStatus.Unchanged, FileOverlayStatus.Normal);
361
            this.StatusKeeper.SetNetworkState(path, NetworkState.None);
362
            Workflow.RaiseChangeNotification(path);
363
        }
364 471

  
365 472

  
366
    }
473
                //Does the object exist or not?
367 474

  
368
    public class CloudAction
369
    {
370
        public CloudActionType Action { get; set; }
371
        public FileInfo LocalFile { get; set; }
372
        public ObjectInfo CloudFile { get; set; }
475
                //Calculate or load the file's hashmap                
373 476

  
374
        public Lazy<string> LocalHash { get; set; }
477
                /*
478
                 if (fileInfo.Bytes > _blockSize)
479
                 {
480
                  var hashPath = Path.Combine(FileAgent.FragmentsPath, relativePath) + ".hashmap";
481
                
482
                //Load the existing hashPath, if we have one, or calculate a new one
483
                var treeHash = File.Exists(hashPath) ?
484
                    TreeHash.LoadTreeHash(hashPath, Guid.NewGuid()).Result 
485
                    : Signature.CalculateTreeHash(fileInfo.FullName, _blockSize , _blockHash);
486
                                
487
                //Do the Hashmap Put
488
                Task<string> putHashMap=CloudClient.PutHashMap(_pithosContainer,url, treeHash);
489
                putHashMap.Wait();
490
                 }*/                    
375 491

  
376
        public string OldFileName { get; set; }
377
        public string OldPath { get; set; }
378
        public string NewFileName { get; set; }
379
        public string NewPath { get; set; }
492
            //
380 493

  
381
        public CloudAction(CloudActionType action, string oldPath, string oldFileName, string newFileName, string newPath)
382
        {
383
            Action = action;
384
            OldFileName = oldFileName;
385
            OldPath = oldPath;
386
            NewFileName = newFileName;
387
            NewPath = newPath;
388
        }
389 494

  
390
        public CloudAction(CloudActionType action, FileInfo localFile, ObjectInfo cloudFile)
391
        {
392
            Action = action;
393
            LocalFile = localFile;
394
            CloudFile = cloudFile;
395
            if (LocalFile != null)
396
                LocalHash = new Lazy<string>(() => Signature.CalculateMD5(LocalFile.FullName), LazyThreadSafetyMode.ExecutionAndPublication);
495
                var putOrUpdate = Task.Factory.StartNew(() =>
496
                {
497
                    //Mark the file as modified while we upload it
498
                    var setStatus = Task.Factory.StartNew(() =>
499
                        StatusKeeper.SetFileOverlayStatus(fileInfo.FullName,FileOverlayStatus.Modified));
500
                    //And then upload it
501
                    var put = setStatus.ContinueWith(t =>
502
                        CloudClient.PutObject(_pithosContainer,url,fileInfo.FullName, hash));
503
                });
504
                putOrUpdate.Wait();
505
                //If everything succeeds, change the file and overlay status to normal
506
                this.StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
507
            }
508
            //Notify the Shell to update the overlays
509
            NativeMethods.RaiseChangeNotification(fileInfo.FullName);
397 510
        }
398 511

  
399
    }
400 512

  
401
    public enum CloudActionType
402
    {
403
        MustSynch,
404
        UploadUnconditional,
405
        DownloadUnconditional,
406
        DeleteLocal,
407
        DeleteCloud,
408
        RenameCloud
409 513
    }
410 514

  
515
   
516

  
411 517

  
412 518
}
b/trunk/Pithos.Core/Agents/WorkflowAgent.cs
18 18
        public IStatusNotification StatusNotification { get; set; }
19 19
        [Import]
20 20
        public IStatusKeeper StatusKeeper { get; set; }
21
        [Import]
22
        public IPithosWorkflow Workflow { get; set; }
21

  
22
        //We should avoid processing files stored in the Fragments folder
23
        //The Full path to the fragments folder is stored in FragmentsPath
24
        public string FragmentsPath { get; set; }
23 25

  
24 26
        [Import]
25 27
        public NetworkAgent NetworkAgent { get; set; }
......
57 59

  
58 60
        public void RestartInterruptedFiles()
59 61
        {
62
            
60 63
            StatusNotification.NotifyChange("Restart processing interrupted files", TraceLevel.Verbose);
61 64
            var interruptedStates = new[] { FileOverlayStatus.Unversioned, FileOverlayStatus.Modified };
62
            var filesQuery = from state in FileState.Queryable
63
                             where interruptedStates.Contains(state.OverlayStatus)
65

  
66
            var pendingEntries = (from state in FileState.Queryable
67
                                 where interruptedStates.Contains(state.OverlayStatus) &&
68
                                       !state.FilePath.StartsWith(FragmentsPath) &&
69
                                       !state.FilePath.EndsWith(".ignore")
70
                                 select state).ToList();
71
            var staleEntries = from state in pendingEntries
72
                                  where !File.Exists(state.FilePath)
73
                                  select state;
74
            var staleKeys = staleEntries.Select(state=>state.Id);
75
            FileState.DeleteAll(staleKeys);
76

  
77
            var validEntries = from state in pendingEntries.Except(staleEntries)
78
                             where File.Exists(state.FilePath)
64 79
                             select new WorkflowState
65 80
                             {
66 81
                                 Path = state.FilePath.ToLower(),
......
73 88
                                                   WatcherChangeTypes.Created :
74 89
                                                   WatcherChangeTypes.Changed
75 90
                             };
76
            _agent.AddFromEnumerable(filesQuery);
77 91

  
78
        }
92
            _agent.AddFromEnumerable(validEntries);
93

  
79 94

  
95
        }       
80 96

  
81 97
        private void Process(WorkflowState state)
82 98
        {
b/trunk/Pithos.Core/FileState.cs
4 4
// </copyright>
5 5
// -----------------------------------------------------------------------
6 6

  
7
using System.IO;
7 8
using System.Threading.Tasks;
8 9
using Castle.ActiveRecord;
9 10
using Castle.ActiveRecord.Framework;
10 11
using NHibernate.Engine;
11 12
using Pithos.Interfaces;
13
using Pithos.Network;
12 14

  
13 15
namespace Pithos.Core
14 16
{
......
48 50
        [Property]
49 51
        public string TopHash { get; set; }
50 52

  
51
      //  [HasMany(Table = "Tags", Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true)]
53
       [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true,Inverse=true)]
52 54
        public IList<FileTag> Tags
53 55
        {
54 56
            get { return _tags; }   
......
77 79

  
78 80
        public Task<FileState> UpdateHashesAsync()
79 81
        {
80
            
82
            //Skip updating the hash for folders
83
            if (Directory.Exists(FilePath))
84
                return Task.Factory.StartNew(() => this);
85

  
81 86
            return Task.Factory.StartNew(() => { Checksum = Signature.CalculateMD5(FilePath); })
82 87
                .ContinueWith(
83 88
                        t => this);
b/trunk/Pithos.Core/IPithosWorkflow.cs
8 8
        IStatusKeeper StatusKeeper { get; set; }
9 9
        FileStatus SetFileStatus(string path,FileStatus status);
10 10
        void ClearFileStatus(string path);
11
        void RaiseChangeNotification(string path);
11
       // void RaiseChangeNotification(string path);
12 12

  
13 13

  
14 14
    }
b/trunk/Pithos.Core/IStatusKeeper.cs
25 25
        void StoreInfo(string path, ObjectInfo objectInfo);
26 26
        T GetStatus<T>(string path,Func<FileState,T> getter,T defaultValue );
27 27
        void SetStatus(string path, Action<FileState> setter);        
28
        void SetNetworkState(string fileName, NetworkState uploading);
29
        NetworkState GetNetworkState(string fileName);
30 28

  
31 29
        void StartProcessing(CancellationToken token);
32 30

  
33 31
        string BlockHash { get; set; }
32
        int BlockSize { get; set; }
34 33
    }
35 34

  
36 35
    [ContractClassFor(typeof(IStatusKeeper))]
......
115 114
            Contract.Requires(setter != null);
116 115
        }
117 116

  
118
        public void SetNetworkState(string path, NetworkState uploading)
117
        public void SetNetworkState(string path, NetworkOperation operation)
119 118
        {
120 119
            Contract.Requires(!String.IsNullOrWhiteSpace(path));
121 120
            Contract.Requires(Path.IsPathRooted(path));            
122 121
        }
123 122

  
124
        public NetworkState GetNetworkState(string path)
123
        public NetworkOperation GetNetworkState(string path)
125 124
        {
126 125
            Contract.Requires(!String.IsNullOrWhiteSpace(path));
127 126

  
128
            return default(NetworkState);
127
            return default(NetworkOperation);
129 128
        }
130 129

  
131 130
        public void ClearFileStatus(string path)
......
142 141
            Contract.Requires(token != null, "token can't be empty");
143 142
        }
144 143

  
145
        public string BlockHash { get; set; }
146
        
144
        public abstract string BlockHash { get; set; }
145
        public abstract int BlockSize { get; set; }
147 146
    }
148 147
}
b/trunk/Pithos.Core/InMemStatusChecker.cs
6 6
using System.Linq;
7 7
using System.Threading;
8 8
using Pithos.Interfaces;
9
using Pithos.Network;
9 10

  
10 11
namespace Pithos.Core
11 12
{
......
79 80
            throw new NotImplementedException();
80 81
        }
81 82

  
82
        ConcurrentDictionary<string, NetworkState> _networkState = new ConcurrentDictionary<string, NetworkState>();
83
        ConcurrentDictionary<string, NetworkOperation> _networkState = new ConcurrentDictionary<string, NetworkOperation>();
83 84

  
84 85
    
85
        public void SetNetworkState(string path, NetworkState state)
86
        public void SetNetworkState(string path, NetworkOperation operation)
86 87
        {
87
            _networkState[path.ToLower()] = state;
88
            _networkState[path.ToLower()] = operation;
88 89
            //Removing may fail so we store the "None" value anyway
89
            if (state == NetworkState.None)
90
            if (operation == NetworkOperation.None)
90 91
            {
91
                NetworkState oldState;
92
                _networkState.TryRemove(path, out oldState);
92
                NetworkOperation oldOperation;
93
                _networkState.TryRemove(path, out oldOperation);
93 94
            }
94 95
        }
95 96

  
96
        public NetworkState GetNetworkState(string path)
97
        public NetworkOperation GetNetworkState(string path)
97 98
        {
98
            NetworkState state;
99
            if (_networkState.TryGetValue(path, out state))
100
                return state;
101
            return NetworkState.None;
99
            NetworkOperation operation;
100
            if (_networkState.TryGetValue(path, out operation))
101
                return operation;
102
            return NetworkOperation.None;
102 103
        }
103 104

  
104 105
        public void StartProcessing(CancellationToken token)
......
108 109

  
109 110
        public string BlockHash { get; set; }
110 111

  
112
        public int BlockSize { get; set; }
113
        
114

  
111 115
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
112 116
        {
113 117
            _overlayCache[path] = overlayStatus;
b/trunk/Pithos.Core/NativeMethods.cs
1 1
๏ปฟusing System;
2 2
using System.Collections.Generic;
3
using System.IO;
3 4
using System.Linq;
4 5
using System.Runtime.InteropServices;
5 6
using System.Text;
......
26 27
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>.</para>
27 28
    /// </remarks>
28 29
    [Flags]
29
    enum HChangeNotifyEventID
30
    public enum HChangeNotifyEventID
30 31
    {
31 32
        /// <summary>
32 33
        /// All events have occurred. 
......
268 269

  
269 270
    #endregion
270 271

  
271

  
272
    internal static class NativeMethods
272
    public static class NativeMethods
273 273
    {
274 274
        [DllImport("shell32.dll")]
275 275
        public static extern void SHChangeNotify(HChangeNotifyEventID wEventId,
......
277 277
                                           IntPtr dwItem1,
278 278
                                           IntPtr dwItem2);
279 279

  
280
        public static void RaiseChangeNotification(string path)
281
        {
282
            if (String.IsNullOrWhiteSpace(path))
283
                throw new ArgumentNullException("path", "The path parameter must not be emtpy");
284

  
285
            if (!Directory.Exists(path) && !File.Exists(path))
286
                throw new FileNotFoundException("The specified file or path does not exist", path);
287

  
288

  
289
            IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
290

  
291
            try
292
            {
293
                NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
294
                                             HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
295
                                             pathPointer, IntPtr.Zero);
296
            }
297
            finally
298
            {
299
                Marshal.FreeHGlobal(pathPointer);
300
            }
301

  
302
        }
280 303
    }
304

  
305

  
281 306
}
b/trunk/Pithos.Core/Pithos.Core.csproj
128 128
  </ItemGroup>
129 129
  <ItemGroup>
130 130
    <Compile Include="Agent.cs" />
131
    <Compile Include="Agents\FileWatcherAgent.cs" />
131
    <Compile Include="Agents\CloudAction.cs" />
132
    <Compile Include="Agents\FileAgent.cs" />
133
    <Compile Include="Agents\FileInfoExtensions.cs" />
132 134
    <Compile Include="Agents\NetworkAgent.cs" />
133 135
    <Compile Include="Agents\WorkflowAgent.cs" />
134 136
    <Compile Include="DynamicDictionary.cs" />
......
136 138
    <Compile Include="FileState.cs" />
137 139
    <Compile Include="IStatusService.cs" />
138 140
    <Compile Include="JobQueue.cs" />
139
    <Compile Include="Signature.cs" />
141
    <Compile Include="NetworkGate.cs" />
140 142
    <Compile Include="StatusKeeper.cs" />
141 143
    <Compile Include="IPithosWorkflow.cs" />
142 144
    <Compile Include="IStatusKeeper.cs" />
......
147 149
    <Compile Include="InMemStatusChecker.cs" />
148 150
    <Compile Include="StatusInfo.cs" />
149 151
    <Compile Include="StatusService.cs" />
150
    <Compile Include="TreeHash.cs" />
151 152
    <Compile Include="WorkflowState.cs" />
152 153
  </ItemGroup>
153 154
  <ItemGroup>
b/trunk/Pithos.Core/PithosMonitor.cs
17 17
using Pithos.Core.Agents;
18 18
using Pithos.Interfaces;
19 19
using System.ServiceModel;
20
using Pithos.Network;
20 21

  
21 22
namespace Pithos.Core
22 23
{
......
26 27
        private const string PithosContainer = "pithos";
27 28
        private const string TrashContainer = "trash";
28 29

  
30
        private const string FragmentsFolder = "fragments";
31

  
32
        private int _blockSize;
33
        private string _blockHash;
34

  
29 35
        [Import]
30 36
        public IPithosSettings Settings{get;set;}
31 37

  
......
44 50
        public IStatusNotification StatusNotification { get; set; }
45 51

  
46 52
        [Import]
47
        public FileWatcherAgent FileWatcherAgent { get; set; }
53
        public FileAgent FileAgent { get; set; }
48 54
        
49 55
        [Import]
50 56
        public WorkflowAgent WorkflowAgent { get; set; }
......
61 67

  
62 68
        public bool Pause
63 69
        {
64
            get { return FileWatcherAgent.Pause; }
70
            get { return FileAgent.Pause; }
65 71
            set
66 72
            {
67
                FileWatcherAgent.Pause = value;
73
                FileAgent.Pause = value;
68 74
                if (value)
69 75
                {
70 76
                    StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused);
......
101 107
            
102 108
            EnsurePithosContainers();
103 109
            
104
            StatusKeeper.BlockHash = "sha256";
110
            StatusKeeper.BlockHash = _blockHash;
111
            StatusKeeper.BlockSize = _blockSize;
105 112
            
106 113
            StatusKeeper.StartProcessing(_cancellationSource.Token);
107 114
            IndexLocalFiles(RootPath);
......
117 124
            CloudClient.AuthenticationUrl = this.AuthenticationUrl;
118 125
            CloudClient.Authenticate(UserName, ApiKey);
119 126

  
120
            var pithosContainers = new[] {PithosContainer, TrashContainer};
127
            var pithosContainers = new[] { TrashContainer,PithosContainer};
121 128
            foreach (var container in pithosContainers)
122 129
            {
123
                if (!CloudClient.ContainerExists(container))
124
                    CloudClient.CreateContainer(container);                
130
                var info=CloudClient.GetContainerInfo(container);
131
                if (info == ContainerInfo.Empty)
132
                {
133
                    CloudClient.CreateContainer(container);
134
                    info = CloudClient.GetContainerInfo(container);
135
                }
136
                _blockSize = info.BlockSize;
137
                _blockHash = info.BlockHash;
125 138
            }
139

  
140
            var allContainers= CloudClient.ListContainers();
141
            var extraContainers = from container in allContainers
142
                                  where !pithosContainers.Contains(container.Name.ToLower())
143
                                      select container;
144

  
145

  
146

  
126 147
        }
127 148

  
128 149
        public string AuthenticationUrl { get; set; }
......
152 173
            Trace.TraceInformation("[START] Index Local");
153 174
            try
154 175
            {
176
                var fragmentsPath=Path.Combine(RootPath, FragmentsFolder);
155 177
                var files =
156 178
                    from filePath in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).AsParallel()
179
                    where !filePath.StartsWith(fragmentsPath,StringComparison.InvariantCultureIgnoreCase) &&
180
                            !filePath.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase)
157 181
                    select filePath.ToLower();
158 182
                StatusKeeper.StoreUnversionedFiles(files);
159 183
                
......
227 251
                StartNetworkAgent(RootPath);
228 252

  
229 253
                WorkflowAgent.StatusNotification = StatusNotification;
254
                WorkflowAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
230 255
                WorkflowAgent.Start();                
231 256
            }
232 257
            catch (Exception)
......
271 296
        private void StartNetworkAgent(string accountPath)
272 297
        {
273 298
            NetworkAgent.StatusNotification = StatusNotification;
274
            
275
            NetworkAgent.Start(RootPath, PithosContainer,TrashContainer);
299

  
300
            NetworkAgent.Start(PithosContainer, TrashContainer,_blockSize,_blockHash);
276 301

  
277 302
            NetworkAgent.ProcessRemoteFiles(accountPath);
278 303
        }
279 304

  
305
        //Make sure a hidden fragments folder exists to store partial downloads
306
        private static string CreateHiddenFolder(string rootPath, string folderName)
307
        {
308
            if (String.IsNullOrWhiteSpace(rootPath))
309
                throw new ArgumentNullException("rootPath");
310
            if (!Path.IsPathRooted(rootPath))
311
                throw new ArgumentException("rootPath");
312
            if (String.IsNullOrWhiteSpace(folderName))
313
                throw new ArgumentNullException("folderName");
314
            Contract.EndContractBlock();
315

  
316
            var folder = Path.Combine(rootPath, folderName);
317
            if (!Directory.Exists(folder))
318
            {
319
                var info = Directory.CreateDirectory(folder);
320
                info.Attributes |= FileAttributes.Hidden;
321

  
322
                Trace.TraceInformation("Created Fragments Folder: {0}", folder);
323
            }
324
            return folder;
325
        }
326

  
280 327
       
281 328

  
282 329

  
283 330
        private void StartWatcherAgent(string path)
284 331
        {
285
            FileWatcherAgent.StatusKeeper = StatusKeeper;
286
            FileWatcherAgent.Workflow = Workflow;
287
            FileWatcherAgent.Start(path);
332
            FileAgent.StatusKeeper = StatusKeeper;
333
            FileAgent.Workflow = Workflow;
334
            FileAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
335
            FileAgent.Start(path);
288 336
        }
289 337

  
290 338
        public void Stop()
291 339
        {            
292
            FileWatcherAgent.Stop();
340
            FileAgent.Stop();
293 341
            if (timer != null)
294 342
                timer.Dispose();
295 343
            timer = null;
b/trunk/Pithos.Core/PithosWorkflow.cs
8 8
using System.Threading.Tasks;
9 9
using Pithos.Interfaces;
10 10
using System.IO;
11
using System.Diagnostics;
11 12

  
12 13
namespace Pithos.Core
13 14
{
......
22 23

  
23 24
        public FileStatus SetFileStatus(string path, FileStatus status)
24 25
        {
26
            Debug.Assert(!path.Contains("fragments"));
27
            Debug.Assert(!path.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase));
28

  
25 29
            if (String.IsNullOrWhiteSpace(path))
26 30
                throw new ArgumentNullException("path", "The path parameter must not be emtpy");
27 31

  
......
57 61

  
58 62
            StatusKeeper.ClearFileStatus(path);
59 63
        }
60

  
61
        public void RaiseChangeNotification(string path)
62
        {
63
            if (String.IsNullOrWhiteSpace(path))
64
                throw new ArgumentNullException("path", "The path parameter must not be emtpy");
65
            
66
            if (!Directory.Exists(path ) && !File.Exists(path))
67
                throw new FileNotFoundException("The specified file or path does not exist",path);
68
            
69

  
70
            IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
71

  
72
            try
73
            {
74
                NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
75
                                             HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
76
                                             pathPointer, IntPtr.Zero);                
77
            }
78
            finally
79
            {
80
                Marshal.FreeHGlobal(pathPointer);
81
            }
82

  
83
        }
64
       
84 65

  
85 66
        public Task<FileStream> OpenStreamWithWaiting(string path)
86 67
        {
b/trunk/Pithos.Core/StatusKeeper.cs
1 1
using System;
2 2
using System.Collections;
3
using System.Collections.Concurrent;
4 3
using System.Collections.Generic;
5 4
using System.ComponentModel.Composition;
6 5
using System.Diagnostics;
......
17 16
using NHibernate.Criterion;
18 17
using NHibernate.Impl;
19 18
using Pithos.Interfaces;
19
using Pithos.Network;
20 20

  
21 21
namespace Pithos.Core
22 22
{
......
44 44
            ;
45 45
            if (!File.Exists(Path.Combine(_pithosDataPath ,"pithos.db")))
46 46
                ActiveRecordStarter.CreateSchema();
47
            
48
            CleanupStaleStates();
47 49

  
48
            BlockHash = "sha256";
49
            BlockSize = 4194304;
50
        }
50 51

  
52
        private void CleanupStaleStates()
53
        {
54
            /*var stales = from state in FileState.Queryable
55
                         where state.FilePath.StartsWith(@"e:\pithos\fragments")
56
                         select state.Id;*/
57
            FileState.DeleteAll(@"FilePath like 'e:\pithos\fragments%'");
58
            ;
51 59
        }
52 60

  
53 61
        private static InPlaceConfigurationSource GetConfiguration(string pithosDbPath)
......
89 97
                    job.ContinueWith(t =>
90 98
                    {
91 99
                        var action = job.Result;
92
                        action();
100
                        try
101
                        {
102
                            action();
103
                        }
104
                        catch (Exception ex)
105
                        {
106
                            Trace.TraceError("[ERROR] STATE \n{0}",ex);
107
                        }
93 108
                        queue.DoAsync(loop);
94 109
                    });
95 110
                };
......
163 178
        }
164 179

  
165 180

  
166
        ConcurrentDictionary<string,NetworkState> _networkState=new ConcurrentDictionary<string, NetworkState>();
167 181
        private string _pithosDataPath;
168 182

  
169
        public void SetNetworkState(string path,NetworkState state)
170
        {
171
            if (String.IsNullOrWhiteSpace(path))
172
                throw new ArgumentNullException("path");
173
            if (!Path.IsPathRooted(path))
174
                throw new ArgumentException("path must be a rooted path","path");
175
            Contract.EndContractBlock();
176

  
177
            _networkState[path.ToLower()] = state;
178
            //Removing may fail so we store the "None" value anyway
179
            if (state == NetworkState.None)
180
            {
181
                NetworkState oldState;
182
                _networkState.TryRemove(path, out oldState);
183
            }
184
        }
185

  
186
        public NetworkState GetNetworkState(string path)
187
        {
188
            if (String.IsNullOrWhiteSpace(path))
189
                throw new ArgumentNullException("path");
190
            if (!Path.IsPathRooted(path))
191
                throw new ArgumentException("path must be a rooted path", "path");
192
            Contract.EndContractBlock();
193

  
194
            NetworkState state;
195
            if (_networkState.TryGetValue(path, out state))
196
                return state;
197
            return NetworkState.None;
198
        }
199

  
200 183
        public T GetStatus<T>(string path,Func<FileState,T> getter,T defaultValue )
201 184
        {
202 185
            if (String.IsNullOrWhiteSpace(path))
......
233 216

  
234 217
            _persistenceAgent.Post(() =>
235 218
            {
236
                var filePath = path.ToLower();
237
                var state = FileState.Queryable.FirstOrDefault(s => s.FilePath == filePath);
238
                if (state != null)
219
                using (new SessionScope())
239 220
                {
240
                    setter(state);
241
                    state.Save();
242
                }
243
                else
244
                {
245
                    state = new FileState {FilePath = filePath};
246
                    setter(state);
247
                    state.Save();
221
                    var filePath = path.ToLower();
222
                    var state = FileState.Queryable.FirstOrDefault(s => s.FilePath == filePath);
223
                    if (state != null)
224
                    {
225
                        setter(state);
226
                        state.Save();
227
                    }
228
                    else
229
                    {
230
                        state = new FileState {FilePath = filePath};
231
                        setter(state);
232
                        state.Save();
233
                    }                    
248 234
                }
249 235
            });
250 236
        }
......
256 242
        /// <param name="setter"></param>
257 243
        private void UpdateStatus(string path, Action<FileState> setter)
258 244
        {
245
            Debug.Assert(!path.Contains("fragments"));
246
            Debug.Assert(!path.EndsWith(".ignore"));
247

  
259 248
            if (String.IsNullOrWhiteSpace(path))
260 249
                throw new ArgumentNullException("path", "path can't be empty");
261 250

  
......
264 253

  
265 254
            _persistenceAgent.Post(() =>
266 255
            {
267
                var filePath = path.ToLower();
268

  
269
                var state = FileState.Queryable.FirstOrDefault(s=> s.FilePath == filePath);
270
                if (state == null)
256
                using (new SessionScope())
271 257
                {
272
                    Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
273
                    return;
258
                    var filePath = path.ToLower();
259

  
260
                    var state = FileState.Queryable.FirstOrDefault(s => s.FilePath == filePath);
261
                    if (state == null)
262
                    {
263
                        Trace.TraceWarning("[NOFILE] Unable to set status for {0}.", filePath);
264
                        return;
265
                    }
266
                    setter(state);
267
                    state.Save();
274 268
                }
275
                setter(state);
276
                state.Save();
269
                
277 270
            });
278 271
        }
279 272

  
......
345 338
                throw new ArgumentNullException("path", "path can't be empty");
346 339
            if (objectInfo==null)
347 340
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
348
            
349
            var state = FileState.Queryable.FirstOrDefault(s => s.FilePath == path);
350
            state = state??new FileState();
351
            
352
            state.FilePath = path.ToLower();
353
            state.Checksum = objectInfo.Hash;
354
            state.FileStatus = FileStatus.Unchanged;
355
            state.OverlayStatus = FileOverlayStatus.Normal;
356
            foreach (var tag in objectInfo.Tags)
341

  
342

  
343
            _persistenceAgent.Post(() =>
357 344
            {
358
                state.Tags.Add(new FileTag
359
                                   {
360
                                       FileState=state,
361
                                       Value=tag.Value
362
                                   });
363
            }
364
            
365
            state.Save();
366
            
345
                var filePath = path.ToLower();
346
                //Load the existing files state and set its properties in one session            
347
                using (new SessionScope())
348
                {
349
                    //Forgetting to use a sessionscope results in two sessions being created, one by 
350
                    //FirstOrDefault and one by Save()
351
                    var state =
352
                        FileState.Queryable.FirstOrDefault(s => s.FilePath == filePath);
353
                    //Create a new empty state object if this is a new file
354
                    state = state ?? new FileState();
355

  
356
                    state.FilePath = filePath;
357
                    state.Checksum = objectInfo.Hash;
358
                    state.FileStatus = FileStatus.Unchanged;
359
                    state.OverlayStatus = FileOverlayStatus.Normal;
360
                    //Create a list of tags from the ObjectInfo's tag dictionary
361
                    //Make sure to bind each tag to its parent state so we don't have to save each tag separately
362
                    //state.Tags = (from pair in objectInfo.Tags
363
                    //                select
364
                    //                    new FileTag
365
                    //                        {
366
                    //                            FileState = state,
367
                    //                            Name = pair.Key,
368
                    //                            Value = pair.Value
369
                    //                        }
370
                    //            ).ToList();
371

  
372
                    //Do the save
373
                    state.Save();
374
                }
375
            });
376

  
367 377
        }
368 378

  
369 379
        
......
386 396
        }
387 397

  
388 398
        public void UpdateFileChecksum(string path, string checksum)
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff