Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / FileState.cs @ 255f5f86

History | View | Annotate | Download (16.4 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 Pithos.Core.Agents;
48
using Pithos.Interfaces;
49
using Pithos.Network;
50
using log4net;
51

    
52
namespace Pithos.Core
53
{
54
    using System;
55
    using System.Collections.Generic;
56
    using System.Linq;
57

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

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

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

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

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

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

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

    
84

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

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

    
91

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

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

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

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

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

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

    
114

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

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

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

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

    
143
                
144

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

    
153

    
154
        }*/
155

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

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

    
171
        }*/
172

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

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

    
200
        }
201

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

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

    
230
        }
231

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

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

    
251
        }
252
*/
253

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

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

    
272
        }
273

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

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

    
292
        }
293

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

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

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

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

    
325
        }
326

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

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

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

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

    
365

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

    
374

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

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

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

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

    
396
            Checksum = hash;
397

    
398
            return this;
399
        }
400

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

    
422
    [ActiveRecord("Tags")]
423
    public class FileTag : ActiveRecordLinqBase<FileTag>
424
    {
425
        [PrimaryKey]
426
        public int Id { get; set; }
427

    
428
        [Property]
429
        public string Name { get; set; }
430

    
431
        [Property]
432
        public string Value { get; set; }
433

    
434
        [BelongsTo("FileStateId")]
435
        public FileState FileState { get; set; }
436

    
437
    }
438
   
439
}