root / trunk / Pithos.Core / FileState.cs @ 540b8cf8
History | View | Annotate | Download (16.3 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 Pithos.Network; |
47 |
using log4net; |
48 |
|
49 |
namespace Pithos.Core |
50 |
{ |
51 |
using System; |
52 |
using System.Collections.Generic; |
53 |
using System.Linq; |
54 |
|
55 |
/// <summary> |
56 |
/// TODO: Update summary. |
57 |
/// </summary> |
58 |
[ActiveRecord] |
59 |
public class FileState : ActiveRecordLinqBase<FileState> |
60 |
{ |
61 |
private static readonly ILog Log = LogManager.GetLogger("FileState"); |
62 |
|
63 |
private IList<FileTag> _tags = new List<FileTag>(); |
64 |
|
65 |
[PrimaryKey(PrimaryKeyType.Guid)] |
66 |
public Guid Id { get; set; } |
67 |
|
68 |
|
69 |
[Property(Unique = true, UniqueKey = "IX_FileState_FilePath")] |
70 |
public string FilePath { get; set; } |
71 |
|
72 |
[Property] |
73 |
public FileOverlayStatus OverlayStatus { get; set; } |
74 |
|
75 |
[Property] |
76 |
public FileStatus FileStatus { get; set; } |
77 |
|
78 |
[Property] |
79 |
public string Checksum { get; set; } |
80 |
|
81 |
|
82 |
[Property] |
83 |
public long? Version { get; set; } |
84 |
|
85 |
[Property] |
86 |
public DateTime? VersionTimeStamp { get; set; } |
87 |
|
88 |
|
89 |
[Property] |
90 |
public bool IsShared { get; set; } |
91 |
|
92 |
[Property] |
93 |
public string SharedBy { get; set; } |
94 |
|
95 |
[Property] |
96 |
public bool ShareWrite { get; set; } |
97 |
|
98 |
[Property] |
99 |
public bool IsFolder{ get; set; } |
100 |
|
101 |
[HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)] |
102 |
public IList<FileTag> Tags |
103 |
{ |
104 |
get { return _tags; } |
105 |
set { _tags = value; } |
106 |
} |
107 |
|
108 |
[Property] |
109 |
public DateTime Modified { get; set; } |
110 |
|
111 |
|
112 |
public FileSystemInfo GetFileSystemInfo() |
113 |
{ |
114 |
if (String.IsNullOrWhiteSpace(FilePath)) |
115 |
throw new InvalidOperationException(); |
116 |
Contract.EndContractBlock(); |
117 |
|
118 |
return Directory.Exists(FilePath) ? |
119 |
(FileSystemInfo)new DirectoryInfo(FilePath) |
120 |
: new FileInfo(FilePath); |
121 |
} |
122 |
|
123 |
public string GetRelativeUrl(AccountInfo accountInfo) |
124 |
{ |
125 |
if (accountInfo==null) |
126 |
throw new ArgumentNullException("accountInfo"); |
127 |
Contract.EndContractBlock(); |
128 |
|
129 |
var fsi=GetFileSystemInfo(); |
130 |
return fsi.AsRelativeUrlTo(accountInfo.AccountPath); |
131 |
} |
132 |
/*public static FileState FindByFilePath(string absolutePath) |
133 |
{ |
134 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
135 |
throw new ArgumentNullException("absolutePath"); |
136 |
Contract.EndContractBlock(); |
137 |
try |
138 |
{ |
139 |
|
140 |
|
141 |
|
142 |
return Queryable.FirstOrDefault(s => s.FilePath == absolutePath); |
143 |
} |
144 |
catch (Exception ex) |
145 |
{ |
146 |
Log.Error(ex.ToString()); |
147 |
throw; |
148 |
} |
149 |
|
150 |
|
151 |
}*/ |
152 |
|
153 |
/* public static void DeleteByFilePath(string absolutePath) |
154 |
{ |
155 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
156 |
throw new ArgumentNullException("absolutePath"); |
157 |
Contract.EndContractBlock(); |
158 |
|
159 |
ExecuteWithRetry((session, instance) => |
160 |
{ |
161 |
const string hqlDelete = "delete FileState where FilePath = :path"; |
162 |
var deletedEntities = session.CreateQuery(hqlDelete) |
163 |
.SetString("path", absolutePath) |
164 |
.ExecuteUpdate(); |
165 |
return deletedEntities; |
166 |
}, null); |
167 |
|
168 |
}*/ |
169 |
|
170 |
public static void StoreFileStatus(string absolutePath, FileStatus newStatus) |
171 |
{ |
172 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
173 |
throw new ArgumentNullException("absolutePath"); |
174 |
Contract.EndContractBlock(); |
175 |
|
176 |
ExecuteWithRetry((session, instance) => |
177 |
{ |
178 |
const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path "; |
179 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
180 |
.SetString("path", absolutePath) |
181 |
.SetEnum("status", newStatus) |
182 |
.ExecuteUpdate(); |
183 |
if (updatedEntities == 0) |
184 |
{ |
185 |
var newState = new FileState |
186 |
{ |
187 |
FilePath = absolutePath, |
188 |
Id = Guid.NewGuid(), |
189 |
FileStatus = newStatus, |
190 |
IsFolder=Directory.Exists(absolutePath) |
191 |
}; |
192 |
newState.CreateAndFlush(); |
193 |
} |
194 |
return null; |
195 |
}, null); |
196 |
|
197 |
} |
198 |
|
199 |
public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus) |
200 |
{ |
201 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
202 |
throw new ArgumentNullException("absolutePath"); |
203 |
Contract.EndContractBlock(); |
204 |
|
205 |
ExecuteWithRetry((session, instance) => |
206 |
{ |
207 |
const string hqlUpdate = |
208 |
"update FileState set OverlayStatus= :status where FilePath = :path "; |
209 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
210 |
.SetString("path", absolutePath) |
211 |
.SetEnum("status", newStatus) |
212 |
.ExecuteUpdate(); |
213 |
if (updatedEntities == 0) |
214 |
{ |
215 |
var newState = new FileState |
216 |
{ |
217 |
FilePath = absolutePath, |
218 |
Id = Guid.NewGuid(), |
219 |
OverlayStatus = newStatus, |
220 |
IsFolder=Directory.Exists(absolutePath) |
221 |
}; |
222 |
newState.CreateAndFlush(); |
223 |
} |
224 |
return null; |
225 |
}, null); |
226 |
|
227 |
} |
228 |
|
229 |
/* |
230 |
public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus) |
231 |
{ |
232 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
233 |
throw new ArgumentNullException("absolutePath"); |
234 |
Contract.EndContractBlock(); |
235 |
|
236 |
ExecuteWithRetry((session, instance) => |
237 |
{ |
238 |
const string hqlUpdate = |
239 |
"update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path "; |
240 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
241 |
.SetString("path", absolutePath) |
242 |
.SetEnum("fileStatus", fileStatus) |
243 |
.SetEnum("overlayStatus", overlayStatus) |
244 |
.ExecuteUpdate(); |
245 |
return updatedEntities; |
246 |
}, null); |
247 |
|
248 |
} |
249 |
*/ |
250 |
|
251 |
/* |
252 |
public static void UpdateStatus(string absolutePath, FileStatus fileStatus) |
253 |
{ |
254 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
255 |
throw new ArgumentNullException("absolutePath"); |
256 |
Contract.EndContractBlock(); |
257 |
|
258 |
ExecuteWithRetry((session, instance) => |
259 |
{ |
260 |
const string hqlUpdate = |
261 |
"update FileState set FileStatus= :fileStatus where FilePath = :path "; |
262 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
263 |
.SetString("path", absolutePath) |
264 |
.SetEnum("fileStatus", fileStatus) |
265 |
.ExecuteUpdate(); |
266 |
return updatedEntities; |
267 |
}, null); |
268 |
|
269 |
} |
270 |
|
271 |
*/ |
272 |
public static void RenameState(string oldPath, string newPath) |
273 |
{ |
274 |
if (string.IsNullOrWhiteSpace(oldPath)) |
275 |
throw new ArgumentNullException("oldPath"); |
276 |
Contract.EndContractBlock(); |
277 |
|
278 |
ExecuteWithRetry((session, instance) => |
279 |
{ |
280 |
const string hqlUpdate = |
281 |
"update FileState set FilePath= :newPath where FilePath = :oldPath "; |
282 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
283 |
.SetString("oldPath", oldPath) |
284 |
.SetString("newPath", newPath) |
285 |
.ExecuteUpdate(); |
286 |
return updatedEntities; |
287 |
}, null); |
288 |
|
289 |
} |
290 |
|
291 |
/* public static void UpdateStatus(Guid id, FileStatus fileStatus) |
292 |
{ |
293 |
|
294 |
ExecuteWithRetry((session, instance) => |
295 |
{ |
296 |
const string hqlUpdate = |
297 |
"update FileState set FileStatus= :fileStatus where Id = :id "; |
298 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
299 |
.SetGuid("id", id) |
300 |
.SetEnum("fileStatus", fileStatus) |
301 |
.ExecuteUpdate(); |
302 |
return updatedEntities; |
303 |
}, null); |
304 |
}*/ |
305 |
|
306 |
public static void UpdateChecksum(string absolutePath, string checksum) |
307 |
{ |
308 |
if (string.IsNullOrWhiteSpace(absolutePath)) |
309 |
throw new ArgumentNullException("absolutePath"); |
310 |
Contract.EndContractBlock(); |
311 |
|
312 |
ExecuteWithRetry((session, instance) => |
313 |
{ |
314 |
const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path "; |
315 |
var updatedEntities = session.CreateQuery(hqlUpdate) |
316 |
.SetString("path", absolutePath) |
317 |
.SetString("checksum", checksum) |
318 |
.ExecuteUpdate(); |
319 |
return updatedEntities; |
320 |
}, null); |
321 |
|
322 |
} |
323 |
|
324 |
public static void ChangeRootPath(string oldPath, string newPath) |
325 |
{ |
326 |
if (String.IsNullOrWhiteSpace(oldPath)) |
327 |
throw new ArgumentNullException("oldPath"); |
328 |
if (!Path.IsPathRooted(oldPath)) |
329 |
throw new ArgumentException("oldPath must be an absolute path", "oldPath"); |
330 |
if (string.IsNullOrWhiteSpace(newPath)) |
331 |
throw new ArgumentNullException("newPath"); |
332 |
if (!Path.IsPathRooted(newPath)) |
333 |
throw new ArgumentException("newPath must be an absolute path", "newPath"); |
334 |
Contract.EndContractBlock(); |
335 |
|
336 |
//Ensure the paths end with the same character |
337 |
if (!oldPath.EndsWith("\\")) |
338 |
oldPath = oldPath + "\\"; |
339 |
if (!newPath.EndsWith("\\")) |
340 |
newPath = newPath + "\\"; |
341 |
|
342 |
ExecuteWithRetry((session, instance) => |
343 |
{ |
344 |
const string hqlUpdate = |
345 |
"update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' "; |
346 |
var renames = session.CreateQuery(hqlUpdate) |
347 |
.SetString("oldPath", oldPath) |
348 |
.SetString("newPath", newPath) |
349 |
.ExecuteUpdate(); |
350 |
return renames; |
351 |
}, null); |
352 |
} |
353 |
|
354 |
public static Task<FileState> CreateForAsync(string filePath, int blockSize, string algorithm) |
355 |
{ |
356 |
if (blockSize <= 0) |
357 |
throw new ArgumentOutOfRangeException("blockSize"); |
358 |
if (String.IsNullOrWhiteSpace(algorithm)) |
359 |
throw new ArgumentNullException("algorithm"); |
360 |
Contract.EndContractBlock(); |
361 |
|
362 |
|
363 |
var fileState = new FileState |
364 |
{ |
365 |
FilePath = filePath, |
366 |
OverlayStatus = FileOverlayStatus.Unversioned, |
367 |
FileStatus = FileStatus.Created, |
368 |
Id = Guid.NewGuid() |
369 |
}; |
370 |
|
371 |
|
372 |
return fileState.UpdateHashesAsync(blockSize, algorithm); |
373 |
} |
374 |
|
375 |
public async Task<FileState> UpdateHashesAsync(int blockSize, string algorithm) |
376 |
{ |
377 |
if (blockSize <= 0) |
378 |
throw new ArgumentOutOfRangeException("blockSize"); |
379 |
if (String.IsNullOrWhiteSpace(algorithm)) |
380 |
throw new ArgumentNullException("algorithm"); |
381 |
Contract.EndContractBlock(); |
382 |
|
383 |
//Skip updating the hash for folders |
384 |
if (Directory.Exists(FilePath)) |
385 |
return this; |
386 |
|
387 |
var hash = await TaskEx.Run(() => |
388 |
{ |
389 |
var info = new FileInfo(FilePath); |
390 |
return info.CalculateHash(blockSize, algorithm); |
391 |
}); |
392 |
|
393 |
Checksum = hash; |
394 |
|
395 |
return this; |
396 |
} |
397 |
|
398 |
private static void ExecuteWithRetry(NHibernateDelegate call, object state) |
399 |
{ |
400 |
int retries = 3; |
401 |
while (retries > 0) |
402 |
try |
403 |
{ |
404 |
using (new SessionScope()) |
405 |
{ |
406 |
Execute(call, state); |
407 |
return; |
408 |
} |
409 |
} |
410 |
catch (ActiveRecordException ) |
411 |
{ |
412 |
retries--; |
413 |
if (retries <= 0) |
414 |
throw; |
415 |
} |
416 |
} |
417 |
} |
418 |
|
419 |
[ActiveRecord("Tags")] |
420 |
public class FileTag : ActiveRecordLinqBase<FileTag> |
421 |
{ |
422 |
[PrimaryKey] |
423 |
public int Id { get; set; } |
424 |
|
425 |
[Property] |
426 |
public string Name { get; set; } |
427 |
|
428 |
[Property] |
429 |
public string Value { get; set; } |
430 |
|
431 |
[BelongsTo("FileStateId")] |
432 |
public FileState FileState { get; set; } |
433 |
|
434 |
} |
435 |
|
436 |
} |