Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (15.6 kB)

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

    
38
using System.Data.SQLite;
39
using System.Diagnostics.Contracts;
40
using System.IO;
41
using System.Threading.Tasks;
42
using Castle.ActiveRecord;
43
using Castle.ActiveRecord.Framework;
44
using Pithos.Core.Agents;
45
using Pithos.Interfaces;
46
using log4net;
47

    
48
namespace Pithos.Core
49
{
50
    using System;
51
    using System.Collections.Generic;
52
    using System.Linq;
53

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

    
62
        private string _filePath;
63
        private IList<FileTag> _tags = new List<FileTag>();
64

    
65
        [PrimaryKey(PrimaryKeyType.Guid)]
66
        public Guid Id { get; set; }
67

    
68
        [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
69
        public string FilePath
70
        {
71
            get { return _filePath; }
72
            set { _filePath = value.ToLower(); }
73
        }
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

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

    
109

    
110
        public static FileState FindByFilePath(string absolutePath)
111
        {
112
            if (string.IsNullOrWhiteSpace(absolutePath))
113
                throw new ArgumentNullException("absolutePath");
114
            Contract.EndContractBlock();
115
            try
116
            {
117

    
118
                
119

    
120
                return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
121
            }
122
            catch (Exception ex)
123
            {
124
                Log.Error(ex.ToString());
125
                throw;
126
            }
127

    
128

    
129
        }
130

    
131
       /* public static void DeleteByFilePath(string absolutePath)
132
        {
133
            if (string.IsNullOrWhiteSpace(absolutePath))
134
                throw new ArgumentNullException("absolutePath");
135
            Contract.EndContractBlock();
136

    
137
            ExecuteWithRetry((session, instance) =>
138
                        {
139
                            const string hqlDelete = "delete FileState where FilePath = :path";
140
                            var deletedEntities = session.CreateQuery(hqlDelete)
141
                                .SetString("path", absolutePath.ToLower())
142
                                .ExecuteUpdate();
143
                            return deletedEntities;
144
                        }, null);
145

    
146
        }*/
147

    
148
        public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
149
        {
150
            if (string.IsNullOrWhiteSpace(absolutePath))
151
                throw new ArgumentNullException("absolutePath");
152
            Contract.EndContractBlock();
153

    
154
            ExecuteWithRetry((session, instance) =>
155
                        {
156
                            const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path  ";
157
                            var updatedEntities = session.CreateQuery(hqlUpdate)
158
                                .SetString("path", absolutePath.ToLower())
159
                                .SetEnum("status", newStatus)
160
                                .ExecuteUpdate();
161
                            if (updatedEntities == 0)
162
                            {
163
                                var newState = new FileState
164
                                                   {
165
                                                       FilePath = absolutePath.ToLower(),
166
                                                       Id = Guid.NewGuid(),
167
                                                       FileStatus = newStatus
168
                                                   };
169
                                newState.CreateAndFlush();
170
                            }
171
                            return null;
172
                        }, null);
173

    
174
        }
175

    
176
        public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus 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 =
185
                                "update FileState set OverlayStatus= :status where FilePath = :path  ";
186
                            var updatedEntities = session.CreateQuery(hqlUpdate)
187
                                .SetString("path", absolutePath.ToLower())
188
                                .SetEnum("status", newStatus)
189
                                .ExecuteUpdate();
190
                            if (updatedEntities == 0)
191
                            {
192
                                var newState = new FileState
193
                                                   {
194
                                                       FilePath = absolutePath,
195
                                                       Id = Guid.NewGuid(),
196
                                                       OverlayStatus = newStatus
197
                                                   };
198
                                newState.CreateAndFlush();
199
                            }
200
                            return null;
201
                        }, null);
202

    
203
        }
204

    
205
/*
206
        public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
207
        {
208
            if (string.IsNullOrWhiteSpace(absolutePath))
209
                throw new ArgumentNullException("absolutePath");
210
            Contract.EndContractBlock();
211

    
212
            ExecuteWithRetry((session, instance) =>
213
                        {
214
                            const string hqlUpdate =
215
                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path  ";
216
                            var updatedEntities = session.CreateQuery(hqlUpdate)
217
                                .SetString("path", absolutePath.ToLower())
218
                                .SetEnum("fileStatus", fileStatus)
219
                                .SetEnum("overlayStatus", overlayStatus)
220
                                .ExecuteUpdate();
221
                            return updatedEntities;
222
                        }, null);
223

    
224
        }
225
*/
226

    
227
/*
228
        public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
229
        {
230
            if (string.IsNullOrWhiteSpace(absolutePath))
231
                throw new ArgumentNullException("absolutePath");
232
            Contract.EndContractBlock();
233

    
234
            ExecuteWithRetry((session, instance) =>
235
                        {
236
                            const string hqlUpdate =
237
                                "update FileState set FileStatus= :fileStatus where FilePath = :path  ";
238
                            var updatedEntities = session.CreateQuery(hqlUpdate)
239
                                .SetString("path", absolutePath.ToLower())
240
                                .SetEnum("fileStatus", fileStatus)
241
                                .ExecuteUpdate();
242
                            return updatedEntities;
243
                        }, null);
244

    
245
        }
246

    
247
*/
248
        public static void RenameState(string oldPath, string newPath)
249
        {
250
            if (string.IsNullOrWhiteSpace(oldPath))
251
                throw new ArgumentNullException("oldPath");
252
            Contract.EndContractBlock();
253

    
254
            ExecuteWithRetry((session, instance) =>
255
                        {
256
                            const string hqlUpdate =
257
                                "update FileState set FilePath= :newPath where FilePath = :oldPath  ";
258
                            var updatedEntities = session.CreateQuery(hqlUpdate)
259
                                .SetString("oldPath", oldPath.ToLower())
260
                                .SetString("newPath", newPath.ToLower())
261
                                .ExecuteUpdate();
262
                            return updatedEntities;
263
                        }, null);
264

    
265
        }
266

    
267
     /*   public static void UpdateStatus(Guid id, FileStatus fileStatus)
268
        {
269

    
270
            ExecuteWithRetry((session, instance) =>
271
            {
272
                const string hqlUpdate =
273
                    "update FileState set FileStatus= :fileStatus where Id = :id  ";
274
                var updatedEntities = session.CreateQuery(hqlUpdate)
275
                    .SetGuid("id", id)
276
                    .SetEnum("fileStatus", fileStatus)
277
                    .ExecuteUpdate();
278
                return updatedEntities;
279
            }, null);
280
        }*/
281

    
282
        public static void UpdateChecksum(string absolutePath, string checksum)
283
        {
284
            if (string.IsNullOrWhiteSpace(absolutePath))
285
                throw new ArgumentNullException("absolutePath");
286
            Contract.EndContractBlock();
287

    
288
            ExecuteWithRetry((session, instance) =>
289
                        {
290
                            const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path  ";
291
                            var updatedEntities = session.CreateQuery(hqlUpdate)
292
                                .SetString("path", absolutePath.ToLower())
293
                                .SetString("checksum", checksum)
294
                                .ExecuteUpdate();
295
                            return updatedEntities;
296
                        }, null);
297

    
298
        }
299

    
300
        public static void ChangeRootPath(string oldPath, string newPath)
301
        {
302
            if (String.IsNullOrWhiteSpace(oldPath))
303
                throw new ArgumentNullException("oldPath");
304
            if (!Path.IsPathRooted(oldPath))
305
                throw new ArgumentException("oldPath must be an absolute path", "oldPath");
306
            if (string.IsNullOrWhiteSpace(newPath))
307
                throw new ArgumentNullException("newPath");
308
            if (!Path.IsPathRooted(newPath))
309
                throw new ArgumentException("newPath must be an absolute path", "newPath");
310
            Contract.EndContractBlock();
311

    
312
            //Ensure the paths end with the same character
313
            if (!oldPath.EndsWith("\\"))
314
                oldPath = oldPath + "\\";
315
            if (!newPath.EndsWith("\\"))
316
                newPath = newPath + "\\";
317

    
318
                ExecuteWithRetry((session, instance) =>
319
                            {
320
                                const string hqlUpdate =
321
                                    "update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
322
                                var renames = session.CreateQuery(hqlUpdate)
323
                                    .SetString("oldPath", oldPath.ToLower())
324
                                    .SetString("newPath", newPath.ToLower())
325
                                    .ExecuteUpdate();
326
                                return renames;
327
                            }, null);
328
        }
329

    
330
        public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm)
331
        {
332
            if (blockSize <= 0)
333
                throw new ArgumentOutOfRangeException("blockSize");
334
            if (String.IsNullOrWhiteSpace(algorithm))
335
                throw new ArgumentNullException("algorithm");
336
            Contract.EndContractBlock();
337

    
338

    
339
            var fileState = new FileState
340
                                {
341
                                    FilePath = filePath.ToLower(),
342
                                    OverlayStatus = FileOverlayStatus.Unversioned,
343
                                    FileStatus = FileStatus.Created,
344
                                    Id = Guid.NewGuid()
345
                                };
346

    
347

    
348
            return fileState.UpdateHashesAsync(blockSize, algorithm);
349
        }
350

    
351
        public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm)
352
        {
353
            if (blockSize <= 0)
354
                throw new ArgumentOutOfRangeException("blockSize");
355
            if (String.IsNullOrWhiteSpace(algorithm))
356
                throw new ArgumentNullException("algorithm");
357
            Contract.EndContractBlock();
358

    
359
            //Skip updating the hash for folders
360
            if (Directory.Exists(FilePath))
361
                return this;
362

    
363
            var hash = await TaskEx.Run(() =>
364
                                            {
365
                                                var info = new FileInfo(FilePath);
366
                                                return info.CalculateHash(blockSize, algorithm);
367
                                            });
368

    
369
            Checksum = hash;
370

    
371
            return this;
372
        }
373

    
374
        private static void ExecuteWithRetry(NHibernateDelegate call, object state)
375
        {
376
            int retries = 3;
377
            while (retries > 0)
378
                try
379
                {
380
                    using (new SessionScope())
381
                    {
382
                        Execute(call, state);
383
                    }
384
                }
385
                catch (ActiveRecordException exc)
386
                {
387
                    retries--;
388
                    if (retries <= 0)
389
                        throw;
390
                }
391
                catch (Exception exc)
392
                {
393
                    throw;
394
                }
395

    
396
        }
397
    }
398

    
399
    [ActiveRecord("Tags")]
400
    public class FileTag : ActiveRecordLinqBase<FileTag>
401
    {
402
        [PrimaryKey]
403
        public int Id { get; set; }
404

    
405
        [Property]
406
        public string Name { get; set; }
407

    
408
        [Property]
409
        public string Value { get; set; }
410

    
411
        [BelongsTo("FileStateId")]
412
        public FileState FileState { get; set; }
413

    
414
    }
415
   
416
}