Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / FileState.cs @ 4147814e

History | View | Annotate | Download (18.2 kB)

1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="FileState.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
15
 *   2. Redistributions in binary form must reproduce the above
16
 *      copyright notice, this list of conditions and the following
17
 *      disclaimer in the documentation and/or other materials
18
 *      provided with the distribution.
19
 *
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
 * POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * The views and conclusions contained in the software and
35
 * documentation are those of the authors and should not be
36
 * interpreted as representing official policies, either expressed
37
 * or implied, of GRNET S.A.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42
using System.Diagnostics.Contracts;
43
using System.IO;
44
using System.Threading.Tasks;
45
using Castle.ActiveRecord;
46
using Castle.ActiveRecord.Framework;
47
using Castle.ActiveRecord.Queries;
48
using NHibernate.Criterion;
49
using Pithos.Core.Agents;
50
using Pithos.Interfaces;
51
using Pithos.Network;
52
using log4net;
53

    
54
namespace Pithos.Core
55
{
56
    using System;
57
    using System.Collections.Generic;    
58

    
59
    /// <summary>
60
    /// TODO: Update summary.
61
    /// </summary>
62
    [ActiveRecord]
63
    public class FileState : ActiveRecordLinqBase<FileState>
64
    {
65
        private static readonly ILog Log = LogManager.GetLogger("FileState");
66

    
67
        private IList<FileTag> _tags = new List<FileTag>();
68

    
69
        [PrimaryKey(PrimaryKeyType.Guid)]
70
        public Guid Id { get; set; }
71

    
72
        
73
        [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
74
        public string FilePath { get; set; }
75

    
76
        [Property]
77
        public FileOverlayStatus OverlayStatus { get; set; }
78

    
79
        [Property]
80
        public FileStatus FileStatus { get; set; }
81

    
82
        [Property]
83
        public string Checksum { get; set; }
84

    
85

    
86
        [Property]
87
        public long? Version { get; set; }
88

    
89
        [Property]
90
        public DateTime? VersionTimeStamp { get; set; }
91

    
92

    
93
        [Property]
94
        public bool IsShared { get; set; }
95

    
96
        [Property]
97
        public string SharedBy { get; set; }
98

    
99
        [Property]
100
        public bool ShareWrite { get; set; }
101

    
102
        [Property]
103
        public bool IsFolder{ get; set; }
104

    
105
        [HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]
106
        public IList<FileTag> Tags
107
        {
108
            get { return _tags; }
109
            set { _tags = value; }
110
        }
111

    
112
        [Property]
113
        public DateTime Modified { get; set; }
114

    
115

    
116
        public FileSystemInfo GetFileSystemInfo()
117
        {
118
            if (String.IsNullOrWhiteSpace(FilePath))
119
                throw new InvalidOperationException();
120
            Contract.EndContractBlock();
121

    
122
            return Directory.Exists(FilePath) ?
123
                (FileSystemInfo)new DirectoryInfo(FilePath)
124
                : new FileInfo(FilePath);
125
        }
126

    
127
        public string GetRelativeUrl(AccountInfo accountInfo)
128
        {
129
            if (accountInfo==null)
130
                throw new ArgumentNullException("accountInfo");
131
            Contract.EndContractBlock();
132

    
133
            var fsi=GetFileSystemInfo();
134
            return fsi.AsRelativeUrlTo(accountInfo.AccountPath);
135
        }
136
        /*public static FileState FindByFilePath(string absolutePath)
137
        {
138
            if (string.IsNullOrWhiteSpace(absolutePath))
139
                throw new ArgumentNullException("absolutePath");
140
            Contract.EndContractBlock();
141
            try
142
            {
143

    
144
                
145

    
146
                return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);
147
            }
148
            catch (Exception ex)
149
            {
150
                Log.Error(ex.ToString());
151
                throw;
152
            }
153

    
154

    
155
        }*/
156

    
157
       /* public static void DeleteByFilePath(string absolutePath)
158
        {
159
            if (string.IsNullOrWhiteSpace(absolutePath))
160
                throw new ArgumentNullException("absolutePath");
161
            Contract.EndContractBlock();
162

    
163
            ExecuteWithRetry((session, instance) =>
164
                        {
165
                            const string hqlDelete = "delete FileState where FilePath = :path";
166
                            var deletedEntities = session.CreateQuery(hqlDelete)
167
                                .SetString("path", absolutePath)
168
                                .ExecuteUpdate();
169
                            return deletedEntities;
170
                        }, null);
171

    
172
        }*/
173

    
174
        public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
175
        {
176
            if (string.IsNullOrWhiteSpace(absolutePath))
177
                throw new ArgumentNullException("absolutePath");
178
            Contract.EndContractBlock();
179

    
180
            ExecuteWithRetry((session, instance) =>
181
                        {
182
                            const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
183
                            var updatedEntities = session.CreateQuery(hqlUpdate)
184
                                .SetString("path", absolutePath)
185
                                .SetEnum("status", newStatus)
186
                                .ExecuteUpdate();
187
                            if (updatedEntities == 0)
188
                            {
189
                                var newState = new FileState
190
                                                   {
191
                                                       FilePath = absolutePath,
192
                                                       Id = Guid.NewGuid(),
193
                                                       FileStatus = newStatus,
194
                                                       IsFolder=Directory.Exists(absolutePath)
195
                                                   };
196
                                newState.CreateAndFlush();
197
                            }
198
                            return null;
199
                        }, null);
200

    
201
        }
202

    
203
        public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
204
        {
205
            if (string.IsNullOrWhiteSpace(absolutePath))
206
                throw new ArgumentNullException("absolutePath");
207
            Contract.EndContractBlock();
208

    
209
            ExecuteWithRetry((session, instance) =>
210
                        {
211
                            const string hqlUpdate =
212
                                "update FileState set OverlayStatus= :status where FilePath = :path ";
213
                            var updatedEntities = session.CreateQuery(hqlUpdate)                                
214
                                .SetString("path", absolutePath)
215
                                .SetEnum("status", newStatus)                                
216
                                .ExecuteUpdate();
217
                            if (updatedEntities == 0)
218
                            {
219
                                var newState = new FileState
220
                                                   {
221
                                                       FilePath = absolutePath,
222
                                                       Id = Guid.NewGuid(),
223
                                                       OverlayStatus = newStatus,
224
                                                       IsFolder=Directory.Exists(absolutePath)
225
                                                   };
226
                                newState.CreateAndFlush();
227
                            }
228
                            return null;
229
                        }, null);
230

    
231
        }
232

    
233
/*
234
        public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
235
        {
236
            if (string.IsNullOrWhiteSpace(absolutePath))
237
                throw new ArgumentNullException("absolutePath");
238
            Contract.EndContractBlock();
239

    
240
            ExecuteWithRetry((session, instance) =>
241
                        {
242
                            const string hqlUpdate =
243
                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path  ";
244
                            var updatedEntities = session.CreateQuery(hqlUpdate)
245
                                .SetString("path", absolutePath)
246
                                .SetEnum("fileStatus", fileStatus)
247
                                .SetEnum("overlayStatus", overlayStatus)
248
                                .ExecuteUpdate();
249
                            return updatedEntities;
250
                        }, null);
251

    
252
        }
253
*/
254

    
255
/*
256
        public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
257
        {
258
            if (string.IsNullOrWhiteSpace(absolutePath))
259
                throw new ArgumentNullException("absolutePath");
260
            Contract.EndContractBlock();
261

    
262
            ExecuteWithRetry((session, instance) =>
263
                        {
264
                            const string hqlUpdate =
265
                                "update FileState set FileStatus= :fileStatus where FilePath = :path  ";
266
                            var updatedEntities = session.CreateQuery(hqlUpdate)
267
                                .SetString("path", absolutePath)
268
                                .SetEnum("fileStatus", fileStatus)
269
                                .ExecuteUpdate();
270
                            return updatedEntities;
271
                        }, null);
272

    
273
        }
274

    
275
*/
276
        public static void RenameState(string oldPath, string newPath)
277
        {
278
            if (string.IsNullOrWhiteSpace(oldPath))
279
                throw new ArgumentNullException("oldPath");
280
            Contract.EndContractBlock();
281

    
282
            ExecuteWithRetry((session, instance) =>
283
                        {
284
                            const string hqlUpdate =
285
                                "update FileState set FilePath= :newPath where FilePath = :oldPath ";
286
                            var updatedEntities = session.CreateQuery(hqlUpdate)
287
                                .SetString("oldPath", oldPath)
288
                                .SetString("newPath", newPath)
289
                                .ExecuteUpdate();
290
                            return updatedEntities;
291
                        }, null);
292

    
293
        }
294

    
295
     /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)
296
        {
297

    
298
            ExecuteWithRetry((session, instance) =>
299
            {
300
                const string hqlUpdate =
301
                    "update FileState set FileStatus= :fileStatus where Id = :id  ";
302
                var updatedEntities = session.CreateQuery(hqlUpdate)
303
                    .SetGuid("id", id)
304
                    .SetEnum("fileStatus", fileStatus)
305
                    .ExecuteUpdate();
306
                return updatedEntities;
307
            }, null);
308
        }*/
309

    
310
        public static void UpdateChecksum(string absolutePath, string checksum)
311
        {
312
            if (string.IsNullOrWhiteSpace(absolutePath))
313
                throw new ArgumentNullException("absolutePath");
314
            Contract.EndContractBlock();
315

    
316
            ExecuteWithRetry((session, instance) =>
317
                        {
318
                            const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
319
                            var updatedEntities = session.CreateQuery(hqlUpdate)
320
                                .SetString("path", absolutePath)
321
                                .SetString("checksum", checksum)
322
                                .ExecuteUpdate();
323
                            return updatedEntities;
324
                        }, null);
325

    
326
        }
327

    
328
        public static void ChangeRootPath(string oldPath, string newPath)
329
        {
330
            if (String.IsNullOrWhiteSpace(oldPath))
331
                throw new ArgumentNullException("oldPath");
332
            if (!Path.IsPathRooted(oldPath))
333
                throw new ArgumentException("oldPath must be an absolute path", "oldPath");
334
            if (string.IsNullOrWhiteSpace(newPath))
335
                throw new ArgumentNullException("newPath");
336
            if (!Path.IsPathRooted(newPath))
337
                throw new ArgumentException("newPath must be an absolute path", "newPath");
338
            Contract.EndContractBlock();
339

    
340
            //Ensure the paths end with the same character
341
            if (!oldPath.EndsWith("\\"))
342
                oldPath = oldPath + "\\";
343
            if (!newPath.EndsWith("\\"))
344
                newPath = newPath + "\\";
345

    
346
                ExecuteWithRetry((session, instance) =>
347
                            {
348
                                const string hqlUpdate =
349
                                    "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
350
                                var renames = session.CreateQuery(hqlUpdate)
351
                                    .SetString("oldPath", oldPath)
352
                                    .SetString("newPath", newPath)
353
                                    .ExecuteUpdate();
354
                                return renames;
355
                            }, null);
356
        }
357

    
358
        public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
359
        {
360
            if (blockSize <= 0)
361
                throw new ArgumentOutOfRangeException("blockSize");
362
            if (String.IsNullOrWhiteSpace(algorithm))
363
                throw new ArgumentNullException("algorithm");
364
            Contract.EndContractBlock();
365

    
366

    
367
            var fileState = new FileState
368
                                {
369
                                    FilePath = filePath,
370
                                    OverlayStatus = FileOverlayStatus.Unversioned,
371
                                    FileStatus = FileStatus.Created,
372
                                    Id = Guid.NewGuid()
373
                                };
374

    
375

    
376
            return fileState.UpdateHashesAsync(blockSize, algorithm);
377
        }
378

    
379
        public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm)
380
        {
381
            if (blockSize <= 0)
382
                throw new ArgumentOutOfRangeException("blockSize");
383
            if (String.IsNullOrWhiteSpace(algorithm))
384
                throw new ArgumentNullException("algorithm");
385
            Contract.EndContractBlock();
386

    
387
            //Skip updating the hash for folders
388
            if (Directory.Exists(FilePath))
389
                return this;
390

    
391
            var hash = await TaskEx.Run(() =>
392
                                            {
393
                                                var info = new FileInfo(FilePath);
394
                                                return info.CalculateHash(blockSize, algorithm);
395
                                            });
396

    
397
            Checksum = hash;
398

    
399
            return this;
400
        }
401

    
402
        private static void ExecuteWithRetry(NHibernateDelegate call, object state)
403
        {
404
            int retries = 3;
405
            while (retries > 0)
406
                try
407
                {
408
                    using (new SessionScope())
409
                    {
410
                        Execute(call, state);
411
                        return;
412
                    }
413
                }
414
                catch (ActiveRecordException )
415
                {
416
                    retries--;
417
                    if (retries <= 0)
418
                        throw;
419
                }
420
        }
421

    
422
        /// <summary>
423
        /// Mark Unversioned all FileState rows from the database whose path
424
        /// starts with one of the removed paths
425
        /// </summary>
426
        /// <param name="removed"></param>
427
        public static void UnversionPaths(List<string> removed)
428
        {
429
            if (removed == null)
430
                return;
431
            if (removed.Count == 0)
432
                return;
433

    
434
            //Create a disjunction (list of OR statements
435
            var disjunction = new Disjunction();            
436
            foreach (var path in removed)
437
            {
438
                //with the restriction FileState.FilePath like '@path%'
439
                disjunction.Add(Restrictions.On<FileState>(s => s.FilePath)
440
                    .IsLike(path, MatchMode.Start));
441
            }
442

    
443
            //Generate a query from the disjunction
444
            var query=QueryOver.Of<FileState>().Where(disjunction);
445
                        
446
            ExecuteWithRetry((session,instance)=>
447
                                 {
448
                                     using (var t=session.BeginTransaction())
449
                                     {
450
                                         var states = query.GetExecutableQueryOver(session).List();
451
                                         foreach (var state in states)
452
                                         {
453
                                             state.FileStatus = FileStatus.Unversioned;
454
                                             state.OverlayStatus = FileOverlayStatus.Unversioned;
455
                                             state.Update();
456
                                         }
457
                                         t.Commit();
458
                                     }
459
                                     return null;
460
                                 },null);
461
        }
462

    
463
    }
464

    
465
    [ActiveRecord("Tags")]
466
    public class FileTag : ActiveRecordLinqBase<FileTag>
467
    {
468
        [PrimaryKey]
469
        public int Id { get; set; }
470

    
471
        [Property]
472
        public string Name { get; set; }
473

    
474
        [Property]
475
        public string Value { get; set; }
476

    
477
        [BelongsTo("FileStateId")]
478
        public FileState FileState { get; set; }
479

    
480
    }
481
   
482
}