Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / StatusAgent.cs @ 4d70073b

History | View | Annotate | Download (29.3 kB)

1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel.Composition;
4
using System.Data.SQLite;
5
using System.Diagnostics;
6
using System.Diagnostics.Contracts;
7
using System.IO;
8
using System.Linq;
9
using System.Text;
10
using System.Threading;
11
using System.Threading.Tasks;
12
using Castle.ActiveRecord;
13
using Castle.ActiveRecord.Framework.Config;
14
using Pithos.Interfaces;
15
using Pithos.Network;
16
using log4net;
17

    
18
namespace Pithos.Core.Agents
19
{
20
    [Export(typeof(IStatusChecker)),Export(typeof(IStatusKeeper))]
21
    public class StatusAgent:IStatusChecker,IStatusKeeper
22
    {
23
        [System.ComponentModel.Composition.Import]
24
        public IPithosSettings Settings { get; set; }
25

    
26
        private Agent<Action> _persistenceAgent;
27

    
28

    
29
        private static readonly ILog Log = LogManager.GetLogger("StatusAgent");
30

    
31
        public StatusAgent()
32
        {            
33
            var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
34
            _pithosDataPath = Path.Combine(appDataPath , "Pithos");
35

    
36
            if (!Directory.Exists(_pithosDataPath))
37
                Directory.CreateDirectory(_pithosDataPath);
38
            var source = GetConfiguration(_pithosDataPath);
39
            ActiveRecordStarter.Initialize(source,typeof(FileState),typeof(FileTag));
40
            ActiveRecordStarter.UpdateSchema();
41
            
42

    
43
            if (!File.Exists(Path.Combine(_pithosDataPath ,"pithos.db")))
44
                ActiveRecordStarter.CreateSchema();
45

    
46
            CreateTrigger();
47
            
48
        }
49

    
50
        private void CreateTrigger()
51
        {
52
            using (var connection = GetConnection())
53
            using (var triggerCommand = connection.CreateCommand())
54
            {
55
                var cmdText = new StringBuilder()
56
                    .AppendLine("CREATE TRIGGER IF NOT EXISTS update_last_modified UPDATE ON FileState FOR EACH ROW")
57
                    .AppendLine("BEGIN")
58
                    .AppendLine("UPDATE FileState SET Modified=datetime('now')  WHERE Id=old.Id;")
59
                    .AppendLine("END;")
60
                    .AppendLine("CREATE TRIGGER IF NOT EXISTS insert_last_modified INSERT ON FileState FOR EACH ROW")
61
                    .AppendLine("BEGIN")
62
                    .AppendLine("UPDATE FileState SET Modified=datetime('now')  WHERE Id=new.Id;")
63
                    .AppendLine("END;")
64
                    .ToString();
65
                triggerCommand.CommandText = cmdText;                
66
                triggerCommand.ExecuteNonQuery();
67
            }
68
        }
69

    
70

    
71
        private static InPlaceConfigurationSource GetConfiguration(string pithosDbPath)
72
        {
73
            if (String.IsNullOrWhiteSpace(pithosDbPath))
74
                throw new ArgumentNullException("pithosDbPath");
75
            if (!Path.IsPathRooted(pithosDbPath))
76
                throw new ArgumentException("path must be a rooted path", "pithosDbPath");
77
            Contract.EndContractBlock();
78

    
79
            var properties = new Dictionary<string, string>
80
                                 {
81
                                     {"connection.driver_class", "NHibernate.Driver.SQLite20Driver"},
82
                                     {"dialect", "NHibernate.Dialect.SQLiteDialect"},
83
                                     {"connection.provider", "NHibernate.Connection.DriverConnectionProvider"},
84
                                     {
85
                                         "proxyfactory.factory_class",
86
                                         "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle"
87
                                         },
88
                                 };
89

    
90
            var connectionString = String.Format(@"Data Source={0}\pithos.db;Version=3;Enlist=N", pithosDbPath);
91
            properties.Add("connection.connection_string", connectionString);
92

    
93
            var source = new InPlaceConfigurationSource();                        
94
            source.Add(typeof (ActiveRecordBase), properties);
95
            source.SetDebugFlag(false);            
96
            return source;
97
        }
98

    
99
        public void StartProcessing(CancellationToken token)
100
        {
101
            _persistenceAgent = Agent<Action>.Start(queue =>
102
            {
103
                Action loop = null;
104
                loop = () =>
105
                {
106
                    var job = queue.Receive();
107
                    job.ContinueWith(t =>
108
                    {
109
                        var action = job.Result;
110
                        try
111
                        {
112
                            action();
113
                        }
114
                        catch (SQLiteException ex)
115
                        {
116
                            Log.ErrorFormat("[ERROR] SQL \n{0}", ex);
117
                        }
118
                        catch (Exception ex)
119
                        {
120
                            Log.ErrorFormat("[ERROR] STATE \n{0}", ex);
121
                        }
122
// ReSharper disable AccessToModifiedClosure
123
                        queue.DoAsync(loop);
124
// ReSharper restore AccessToModifiedClosure
125
                    });
126
                };
127
                loop();
128
            });
129
            
130
        }
131

    
132
       
133

    
134
        public void Stop()
135
        {
136
            _persistenceAgent.Stop();            
137
        }
138
       
139

    
140
        public void ProcessExistingFiles(IEnumerable<FileInfo> existingFiles)
141
        {
142
            if(existingFiles  ==null)
143
                throw new ArgumentNullException("existingFiles");
144
            Contract.EndContractBlock();
145
            
146
            //Find new or matching files with a left join to the stored states
147
            var fileStates = FileState.Queryable;
148
            var currentFiles=from file in existingFiles
149
                      join state in fileStates on file.FullName.ToLower() equals state.FilePath.ToLower() into gs
150
                      from substate in gs.DefaultIfEmpty()
151
                               select new {File = file, State = substate};
152

    
153
            //To get the deleted files we must get the states that have no corresponding
154
            //files. 
155
            //We can't use the File.Exists method inside a query, so we get all file paths from the states
156
            var statePaths = (from state in fileStates
157
                             select new {state.Id, state.FilePath}).ToList();
158
            //and check each one
159
            var missingStates= (from path in statePaths
160
                                where !File.Exists(path.FilePath) && !Directory.Exists(path.FilePath)
161
                               select path.Id).ToList();
162
            //Finally, retrieve the states that correspond to the deleted files            
163
            var deletedFiles = from state in fileStates 
164
                        where missingStates.Contains(state.Id)
165
                        select new { File = default(FileInfo), State = state };
166

    
167
            var pairs = currentFiles.Union(deletedFiles);
168

    
169
            foreach(var pair in pairs)
170
            {
171
                var fileState = pair.State;
172
                var file = pair.File;
173
                if (fileState == null)
174
                {
175
                    //This is a new file
176
                    var fullPath = pair.File.FullName;
177
                    var createState = FileState.CreateForAsync(fullPath, BlockSize, BlockHash);
178
                    createState.ContinueWith(state => _persistenceAgent.Post(state.Result.Create));
179
                }                
180
                else if (file == null)
181
                {
182
                    //This file was deleted while we were down. We should mark it as deleted
183
                    //We have to go through UpdateStatus here because the state object we are using
184
                    //was created by a different ORM session.
185
                    _persistenceAgent.Post(()=> UpdateStatusDirect(fileState.Id, FileStatus.Deleted));                    
186
                }
187
                else
188
                {
189
                    //This file has a matching state. Need to check for possible changes
190
                    var hashString = file.CalculateHash(BlockSize,BlockHash);
191
                    //If the hashes don't match the file was changed
192
                    if (fileState.Checksum != hashString)
193
                    {
194
                        _persistenceAgent.Post(() => UpdateStatusDirect(fileState.Id, FileStatus.Modified));
195
                    }                    
196
                }
197
            };            
198
         
199
        }
200

    
201
        private int UpdateStatusDirect(Guid id, FileStatus status)
202
        {
203
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
204
            {
205

    
206
                try
207
                {
208
                    
209
                    using (var connection = GetConnection())
210
                    using (
211
                        var command = new SQLiteCommand("update FileState set FileStatus= :fileStatus where Id = :id  ",
212
                                                        connection))
213
                    {                                                
214
                        command.Parameters.AddWithValue("fileStatus", status);
215

    
216
                        command.Parameters.AddWithValue("id", id);
217
                        
218
                        var affected = command.ExecuteNonQuery();
219
                        
220
                        return affected;
221
                    }
222

    
223
                }
224
                catch (Exception exc)
225
                {
226
                    Log.Error(exc.ToString());
227
                    throw;
228
                }
229
            }
230
        }
231
        
232
        private int UpdateStatusDirect(string path, FileStatus status)
233
        {
234
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
235
            {
236

    
237
                try
238
                {
239

    
240
                    
241
                    using (var connection = GetConnection())
242
                    using (
243
                        var command =
244
                            new SQLiteCommand("update FileState set FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE",
245
                                              connection))
246
                    {
247

    
248

    
249
                        command.Parameters.AddWithValue("fileStatus", status);
250

    
251
                        command.Parameters.AddWithValue("path", path);
252
                        
253
                        var affected = command.ExecuteNonQuery();
254
                        return affected;
255
                    }
256
                }
257
                catch (Exception exc)
258
                {
259
                    Log.Error(exc.ToString());
260
                    throw;
261
                }
262
            }
263
        }
264

    
265
        private int UpdateStatusDirect(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
266
        {
267
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
268
            {
269

    
270
                try
271
                {
272

    
273
                    
274
                    using (var connection = GetConnection())
275
                    using (
276
                        var command =
277
                            new SQLiteCommand(
278
                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE ",
279
                                connection))
280
                    {
281

    
282
                        command.Parameters.AddWithValue("path", absolutePath);
283
                        command.Parameters.AddWithValue("fileStatus", fileStatus);
284
                        command.Parameters.AddWithValue("overlayStatus", overlayStatus);
285
                        
286
                        var affected = command.ExecuteNonQuery();
287
                        return affected;
288
                    }
289
                }
290
                catch (Exception exc)
291
                {
292
                    Log.Error(exc.ToString());
293
                    throw;
294
                }
295
            }
296
        }
297
        
298

    
299

    
300
        public string BlockHash { get; set; }
301

    
302
        public int BlockSize { get; set; }
303
        public void ChangeRoots(string oldPath, string newPath)
304
        {
305
            if (String.IsNullOrWhiteSpace(oldPath))
306
                throw new ArgumentNullException("oldPath");
307
            if (!Path.IsPathRooted(oldPath))
308
                throw new ArgumentException("oldPath must be an absolute path", "oldPath");
309
            if (string.IsNullOrWhiteSpace(newPath))
310
                throw new ArgumentNullException("newPath");
311
            if (!Path.IsPathRooted(newPath))
312
                throw new ArgumentException("newPath must be an absolute path", "newPath");
313
            Contract.EndContractBlock();
314

    
315
            FileState.ChangeRootPath(oldPath,newPath);
316

    
317
        }
318

    
319
        private PithosStatus _pithosStatus=PithosStatus.InSynch;       
320

    
321
        public void SetPithosStatus(PithosStatus status)
322
        {
323
            _pithosStatus = status;
324
        }
325

    
326
        public PithosStatus GetPithosStatus()
327
        {
328
            return _pithosStatus;
329
        }
330

    
331

    
332
        private readonly string _pithosDataPath;
333

    
334

    
335
        public FileState GetStateByFilePath(string path)
336
        {
337
            if (String.IsNullOrWhiteSpace(path))
338
                throw new ArgumentNullException("path");
339
            if (!Path.IsPathRooted(path))
340
                throw new ArgumentException("The path must be rooted", "path");
341
            Contract.EndContractBlock();
342

    
343
            try
344
            {
345
                
346
                using (var connection = GetConnection())
347
                using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum   ,Version    ,VersionTimeStamp,IsShared   ,SharedBy   ,ShareWrite  from FileState where FilePath=:path COLLATE NOCASE", connection))
348
                {
349
                    
350
                    command.Parameters.AddWithValue("path", path);
351
                    
352
                    using (var reader = command.ExecuteReader())
353
                    {
354
                        if (reader.Read())
355
                        {
356
                            //var values = new object[reader.FieldCount];
357
                            //reader.GetValues(values);
358
                            var state = new FileState
359
                                            {
360
                                                Id = reader.GetGuid(0),
361
                                                FilePath = reader.IsDBNull(1)?"":reader.GetString(1),
362
                                                OverlayStatus =reader.IsDBNull(2)?FileOverlayStatus.Unversioned: (FileOverlayStatus) reader.GetInt64(2),
363
                                                FileStatus = reader.IsDBNull(3)?FileStatus.Missing:(FileStatus) reader.GetInt64(3),
364
                                                Checksum = reader.IsDBNull(4)?"":reader.GetString(4),
365
                                                Version = reader.IsDBNull(5)?default(long):reader.GetInt64(5),
366
                                                VersionTimeStamp = reader.IsDBNull(6)?default(DateTime):reader.GetDateTime(6),
367
                                                IsShared = !reader.IsDBNull(7) && reader.GetBoolean(7),
368
                                                SharedBy = reader.IsDBNull(8)?"":reader.GetString(8),
369
                                                ShareWrite = !reader.IsDBNull(9) && reader.GetBoolean(9)
370
                                            };
371
/*
372
                            var state = new FileState
373
                                            {
374
                                                Id = (Guid) values[0],
375
                                                FilePath = (string) values[1],
376
                                                OverlayStatus = (FileOverlayStatus) (long)values[2],
377
                                                FileStatus = (FileStatus) (long)values[3],
378
                                                Checksum = (string) values[4],
379
                                                Version = (long?) values[5],
380
                                                VersionTimeStamp = (DateTime?) values[6],
381
                                                IsShared = (long)values[7] == 1,
382
                                                SharedBy = (string) values[8],
383
                                                ShareWrite = (long)values[9] == 1
384
                                            };
385
*/
386
                            return state;
387
                        }
388
                        else
389
                        {
390
                            return null;
391
                        }
392

    
393
                    }                    
394
                }
395
            }
396
            catch (Exception exc)
397
            {
398
                Log.ErrorFormat(exc.ToString());
399
                throw;
400
            }            
401
        }
402

    
403
        public FileOverlayStatus GetFileOverlayStatus(string path)
404
        {
405
            if (String.IsNullOrWhiteSpace(path))
406
                throw new ArgumentNullException("path");
407
            if (!Path.IsPathRooted(path))
408
                throw new ArgumentException("The path must be rooted", "path");
409
            Contract.EndContractBlock();
410

    
411
            try
412
            {
413
                
414
                using (var connection = GetConnection())
415
                using (var command = new SQLiteCommand("select OverlayStatus from FileState where FilePath=:path  COLLATE NOCASE", connection))
416
                {
417
                    
418
                    command.Parameters.AddWithValue("path", path);
419
                    
420
                    var s = command.ExecuteScalar();
421
                    return (FileOverlayStatus) Convert.ToInt32(s);
422
                }
423
            }
424
            catch (Exception exc)
425
            {
426
                Log.ErrorFormat(exc.ToString());
427
                return FileOverlayStatus.Unversioned;
428
            }
429
        }
430

    
431
        private string GetConnectionString()
432
        {
433
            var connectionString = String.Format(@"Data Source={0}\pithos.db;Version=3;Enlist=N;Pooling=True", _pithosDataPath);
434
            return connectionString;
435
        }
436

    
437
        private SQLiteConnection GetConnection()
438
        {
439
            var connectionString = GetConnectionString();
440
            var connection = new SQLiteConnection(connectionString);
441
            connection.Open();
442
            using(var cmd =connection.CreateCommand())
443
            {
444
                cmd.CommandText = "PRAGMA journal_mode=WAL";
445
                cmd.ExecuteNonQuery();
446
            }
447
            return connection;
448
        }
449

    
450
        public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus)
451
        {
452
            if (String.IsNullOrWhiteSpace(path))
453
                throw new ArgumentNullException("path");
454
            if (!Path.IsPathRooted(path))
455
                throw new ArgumentException("The path must be rooted","path");
456
            Contract.EndContractBlock();
457

    
458
            _persistenceAgent.Post(() => FileState.StoreOverlayStatus(path,overlayStatus));
459
        }
460

    
461
       /* public void RenameFileOverlayStatus(string oldPath, string newPath)
462
        {
463
            if (String.IsNullOrWhiteSpace(oldPath))
464
                throw new ArgumentNullException("oldPath");
465
            if (!Path.IsPathRooted(oldPath))
466
                throw new ArgumentException("The oldPath must be rooted", "oldPath");
467
            if (String.IsNullOrWhiteSpace(newPath))
468
                throw new ArgumentNullException("newPath");
469
            if (!Path.IsPathRooted(newPath))
470
                throw new ArgumentException("The newPath must be rooted", "newPath");
471
            Contract.EndContractBlock();
472

    
473
            _persistenceAgent.Post(() =>FileState.RenameState(oldPath, newPath));
474
        }*/
475

    
476
        public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
477
        {
478
            if (String.IsNullOrWhiteSpace(path))
479
                throw new ArgumentNullException("path");
480
            if (!Path.IsPathRooted(path))
481
                throw new ArgumentException("The path must be rooted", "path");
482
            Contract.EndContractBlock();
483

    
484
            Debug.Assert(!path.Contains(FolderConstants.CacheFolder));
485
            Debug.Assert(!path.EndsWith(".ignore"));
486

    
487
            _persistenceAgent.Post(() => UpdateStatusDirect(path, fileStatus, overlayStatus));
488
        }
489

    
490
/*
491
        public void StoreInfo(string path,ObjectInfo objectInfo)
492
        {
493
            if (String.IsNullOrWhiteSpace(path))
494
                throw new ArgumentNullException("path");
495
            if (!Path.IsPathRooted(path))
496
                throw new ArgumentException("The path must be rooted", "path");            
497
            if (objectInfo == null)
498
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
499
            Contract.EndContractBlock();
500

    
501
            _persistenceAgent.Post(() =>
502
            {
503
                var filePath = path.ToLower();
504
                //Load the existing files state and set its properties in one session            
505
                using (new SessionScope())
506
                {
507
                    //Forgetting to use a sessionscope results in two sessions being created, one by 
508
                    //FirstOrDefault and one by Save()
509
                    var state =FileState.FindByFilePath(filePath);
510
                    
511
                    //Create a new empty state object if this is a new file
512
                    state = state ?? new FileState();
513

    
514
                    state.FilePath = filePath;
515
                    state.Checksum = objectInfo.Hash;
516
                    state.Version = objectInfo.Version;
517
                    state.VersionTimeStamp = objectInfo.VersionTimestamp;
518

    
519
                    state.FileStatus = FileStatus.Unchanged;
520
                    state.OverlayStatus = FileOverlayStatus.Normal;
521
                    
522
                  
523
                    //Do the save
524
                    state.Save();
525
                }
526
            });
527

    
528
        }
529
*/
530
        
531
        public void StoreInfo(string path, ObjectInfo objectInfo)
532
        {
533
            if (String.IsNullOrWhiteSpace(path))
534
                throw new ArgumentNullException("path");
535
            if (!Path.IsPathRooted(path))
536
                throw new ArgumentException("The path must be rooted", "path");
537
            if (objectInfo == null)
538
                throw new ArgumentNullException("objectInfo", "objectInfo can't be empty");
539
            Contract.EndContractBlock();
540

    
541
            _persistenceAgent.Post(() => StoreInfoDirect(path, objectInfo));
542

    
543
        }
544

    
545
        private void StoreInfoDirect(string path, ObjectInfo objectInfo)
546
        {
547
            try
548
            {
549
                
550
                using (var connection = GetConnection())
551
                using (var command = new SQLiteCommand(connection))
552
                {
553
                    if (StateExists(path, connection))
554
                        command.CommandText =
555
                            "update FileState set FileStatus= :fileStatus where FilePath = :path  COLLATE NOCASE ";
556
                    else
557
                    {
558
                        command.CommandText =
559
                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,FileStatus,OverlayStatus) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:fileStatus,:overlayStatus)";
560
                        command.Parameters.AddWithValue("id", Guid.NewGuid());
561
                    }
562

    
563
                    command.Parameters.AddWithValue("path", path);
564
                    command.Parameters.AddWithValue("checksum", objectInfo.Hash);
565
                    command.Parameters.AddWithValue("version", objectInfo.Version);
566
                    command.Parameters.AddWithValue("versionTimeStamp",
567
                                                    objectInfo.VersionTimestamp);
568
                    command.Parameters.AddWithValue("fileStatus", FileStatus.Unchanged);
569
                    command.Parameters.AddWithValue("overlayStatus",
570
                                                    FileOverlayStatus.Normal);
571

    
572
                    var affected = command.ExecuteNonQuery();
573
                    return;
574
                }
575
            }
576
            catch (Exception exc)
577
            {
578
                Log.Error(exc.ToString());
579
                throw;
580
            }
581
        }
582

    
583
        private bool StateExists(string filePath,SQLiteConnection connection)
584
        {
585
            using (var command = new SQLiteCommand("Select count(*) from FileState where FilePath=:path  COLLATE NOCASE", connection))
586
            {
587
                command.Parameters.AddWithValue("path", filePath);
588
                var result = command.ExecuteScalar();
589
                return ((long)result >= 1);
590
            }
591

    
592
        }
593

    
594
        public void SetFileStatus(string path, FileStatus status)
595
        {
596
            if (String.IsNullOrWhiteSpace(path))
597
                throw new ArgumentNullException("path");
598
            if (!Path.IsPathRooted(path))
599
                throw new ArgumentException("The path must be rooted", "path");
600
            Contract.EndContractBlock();
601
            
602
            _persistenceAgent.Post(() => UpdateStatusDirect(path, status));
603
        }
604

    
605
        public FileStatus GetFileStatus(string path)
606
        {
607
            if (String.IsNullOrWhiteSpace(path))
608
                throw new ArgumentNullException("path");
609
            if (!Path.IsPathRooted(path))
610
                throw new ArgumentException("The path must be rooted", "path");
611
            Contract.EndContractBlock();
612

    
613
            
614
            using (var connection = GetConnection())
615
            {
616
                var command = new SQLiteCommand("select FileStatus from FileState where FilePath=:path  COLLATE NOCASE", connection);
617
                command.Parameters.AddWithValue("path", path);
618
                
619
                var statusValue = command.ExecuteScalar();
620
                if (statusValue==null)
621
                    return FileStatus.Missing;
622
                return (FileStatus)Convert.ToInt32(statusValue);
623
            }
624
        }
625

    
626
        /// <summary>
627
        /// Deletes the status of the specified file
628
        /// </summary>
629
        /// <param name="path"></param>
630
        public void ClearFileStatus(string path)
631
        {
632
            if (String.IsNullOrWhiteSpace(path))
633
                throw new ArgumentNullException("path");
634
            if (!Path.IsPathRooted(path))
635
                throw new ArgumentException("The path must be rooted", "path");
636
            Contract.EndContractBlock();
637

    
638
            _persistenceAgent.Post(() => DeleteDirect(path));   
639
        }
640

    
641
        /// <summary>
642
        /// Deletes the status of the specified folder and all its contents
643
        /// </summary>
644
        /// <param name="path"></param>
645
        public void ClearFolderStatus(string path)
646
        {
647
            if (String.IsNullOrWhiteSpace(path))
648
                throw new ArgumentNullException("path");
649
            if (!Path.IsPathRooted(path))
650
                throw new ArgumentException("The path must be rooted", "path");
651
            Contract.EndContractBlock();
652

    
653
            _persistenceAgent.Post(() => DeleteFolderDirect(path));   
654
        }
655

    
656
        public IEnumerable<FileState> GetChildren(FileState fileState)
657
        {
658
            if (fileState == null)
659
                throw new ArgumentNullException("fileState");
660
            Contract.EndContractBlock();
661

    
662
            var children = from state in FileState.Queryable
663
                           where state.FilePath.StartsWith(fileState.FilePath + "\\")
664
                           select state;
665
            return children;
666
        }
667

    
668
        private int DeleteDirect(string filePath)
669
        {
670
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
671
            {
672

    
673
                try
674
                {
675

    
676
                    
677
                    using (var connection = GetConnection())
678
                    {
679
                        var command = new SQLiteCommand("delete from FileState where FilePath = :path  COLLATE NOCASE",
680
                                                        connection);
681

    
682
                        command.Parameters.AddWithValue("path", filePath);
683
                        
684
                        var affected = command.ExecuteNonQuery();
685
                        return affected;
686
                    }
687
                }
688
                catch (Exception exc)
689
                {
690
                    Log.Error(exc.ToString());
691
                    throw;
692
                }
693
            }
694
        }
695

    
696
        private int DeleteFolderDirect(string filePath)
697
        {
698
            using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
699
            {
700

    
701
                try
702
                {
703

    
704
                    
705
                    using (var connection = GetConnection())
706
                    {
707
                        var command = new SQLiteCommand("delete from FileState where FilePath = :path or FilePath like :path + '/%'  COLLATE NOCASE",
708
                                                        connection);
709

    
710
                        command.Parameters.AddWithValue("path", filePath);
711
                        
712
                        var affected = command.ExecuteNonQuery();
713
                        return affected;
714
                    }
715
                }
716
                catch (Exception exc)
717
                {
718
                    Log.Error(exc.ToString());
719
                    throw;
720
                }
721
            }
722
        }
723

    
724
        public void UpdateFileChecksum(string path, string checksum)
725
        {
726
            if (String.IsNullOrWhiteSpace(path))
727
                throw new ArgumentNullException("path");
728
            if (!Path.IsPathRooted(path))
729
                throw new ArgumentException("The path must be rooted", "path");            
730
            Contract.EndContractBlock();
731

    
732
            _persistenceAgent.Post(() => FileState.UpdateChecksum(path, checksum));
733
        }
734

    
735
    }
736

    
737
   
738
}