Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / FileState.cs @ 3c76f045

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

    
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 IList<FileTag> _tags = new List<FileTag>();
63

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

    
67
        
68
        [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
69
        public string FilePath { get; set; }
70

    
71
        [Property]
72
        public FileOverlayStatus OverlayStatus { get; set; }
73

    
74
        [Property]
75
        public FileStatus FileStatus { get; set; }
76

    
77
        [Property]
78
        public string Checksum { get; set; }
79

    
80

    
81
        [Property]
82
        public long? Version { get; set; }
83

    
84
        [Property]
85
        public DateTime? VersionTimeStamp { get; set; }
86

    
87

    
88
        [Property]
89
        public bool IsShared { get; set; }
90

    
91
        [Property]
92
        public string SharedBy { get; set; }
93

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

    
97
        [Property]
98
        public bool IsFolder{ get; set; }
99

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

    
107

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

    
116
                
117

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

    
126

    
127
        }*/
128

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

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

    
144
        }*/
145

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

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

    
173
        }
174

    
175
        public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus 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 =
184
                                "update FileState set OverlayStatus= :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
                                                       OverlayStatus = newStatus,
196
                                                       IsFolder=Directory.Exists(absolutePath)
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)
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)
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)
260
                                .SetString("newPath", newPath)
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)
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)
324
                                    .SetString("newPath", newPath)
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,
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
                        return;
384
                    }
385
                }
386
                catch (ActiveRecordException )
387
                {
388
                    retries--;
389
                    if (retries <= 0)
390
                        throw;
391
                }
392
        }
393
    }
394

    
395
    [ActiveRecord("Tags")]
396
    public class FileTag : ActiveRecordLinqBase<FileTag>
397
    {
398
        [PrimaryKey]
399
        public int Id { get; set; }
400

    
401
        [Property]
402
        public string Name { get; set; }
403

    
404
        [Property]
405
        public string Value { get; set; }
406

    
407
        [BelongsTo("FileStateId")]
408
        public FileState FileState { get; set; }
409

    
410
    }
411
   
412
}