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