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