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 |
} |