Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / FileState.cs @ db8a9589

History | View | Annotate | Download (18.3 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.Reflection;
45
using System.Threading.Tasks;
46
using Castle.ActiveRecord;
47
using Castle.ActiveRecord.Framework;
48
using Castle.ActiveRecord.Queries;
49
using NHibernate.Criterion;
50
using Pithos.Core.Agents;
51
using Pithos.Interfaces;
52
using Pithos.Network;
53
using log4net;
54

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

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

    
68

    
69
        private IList<FileTag> _tags = new List<FileTag>();
70

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

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

    
78
        [Property]
79
        public FileOverlayStatus OverlayStatus { get; set; }
80

    
81
        [Property]
82
        public FileStatus FileStatus { get; set; }
83

    
84
        [Property]
85
        public string Checksum { get; set; }
86

    
87

    
88
        [Property]
89
        public long? Version { get; set; }
90

    
91
        [Property]
92
        public DateTime? VersionTimeStamp { get; set; }
93

    
94

    
95
        [Property]
96
        public bool IsShared { get; set; }
97

    
98
        [Property]
99
        public string SharedBy { get; set; }
100

    
101
        [Property]
102
        public bool ShareWrite { get; set; }
103

    
104
        [Property]
105
        public bool IsFolder{ get; set; }
106

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

    
114
        [Property]
115
        public DateTime Modified { get; set; }
116

    
117

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

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

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

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

    
146
                
147

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

    
156

    
157
        }*/
158

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

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

    
174
        }*/
175

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

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

    
203
        }
204

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

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

    
233
        }
234

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

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

    
254
        }
255
*/
256

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

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

    
275
        }
276

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

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

    
295
        }
296

    
297
     /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)
298
        {
299

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

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

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

    
328
        }
329

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

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

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

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

    
368

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

    
377

    
378
            return fileState.UpdateHashesAsync(blockSize, algorithm);
379
        }
380

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

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

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

    
399
            Checksum = hash;
400

    
401
            return this;
402
        }
403

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

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

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

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

    
465
    }
466

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

    
473
        [Property]
474
        public string Name { get; set; }
475

    
476
        [Property]
477
        public string Value { get; set; }
478

    
479
        [BelongsTo("FileStateId")]
480
        public FileState FileState { get; set; }
481

    
482
    }
483
   
484
}