Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / FileState.cs @ 759bd3c4

History | View | Annotate | Download (17.1 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
    using System.Linq;
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("FileState");
67

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

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

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

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

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

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

    
86

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

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

    
93

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

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

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

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

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

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

    
116

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

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

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

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

    
145
                
146

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

    
155

    
156
        }*/
157

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

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

    
173
        }*/
174

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

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

    
202
        }
203

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

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

    
232
        }
233

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

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

    
253
        }
254
*/
255

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

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

    
274
        }
275

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

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

    
294
        }
295

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

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

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

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

    
327
        }
328

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

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

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

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

    
367

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

    
376

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

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

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

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

    
398
            Checksum = hash;
399

    
400
            return this;
401
        }
402

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

    
423
        public static void RemovePaths(IEnumerable<string> removed)
424
        {
425
            
426
            var disjunction = new Disjunction();
427

    
428
            foreach (var path in removed)
429
            {
430
                disjunction.Add(Restrictions.On<FileState>(s => s.FilePath).IsLike(path, MatchMode.Start));
431
            }
432

    
433
            
434
            
435
            var query=QueryOver.Of<FileState>().Where(disjunction);
436
            var aq = new ProjectionQuery<FileState,Guid>(query.DetachedCriteria,
437
                        Projections.ProjectionList().Add(Projections.Id()));
438
            var ids=aq.Execute();
439
            FileState.DeleteAll(ids);
440
                
441
        }
442
    }
443

    
444
    [ActiveRecord("Tags")]
445
    public class FileTag : ActiveRecordLinqBase<FileTag>
446
    {
447
        [PrimaryKey]
448
        public int Id { get; set; }
449

    
450
        [Property]
451
        public string Name { get; set; }
452

    
453
        [Property]
454
        public string Value { get; set; }
455

    
456
        [BelongsTo("FileStateId")]
457
        public FileState FileState { get; set; }
458

    
459
    }
460
   
461
}