Use exponential backoff when updating the password or last login time in WebDAV.
[pithos] / src / gr / ebs / gss / server / domain / FileHeader.java
1 /*\r
2  * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.\r
3  *\r
4  * This file is part of GSS.\r
5  *\r
6  * GSS is free software: you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation, either version 3 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * GSS is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.\r
18  */\r
19 package gr.ebs.gss.server.domain;\r
20 \r
21 import gr.ebs.gss.server.domain.dto.FileHeaderDTO;\r
22 \r
23 import java.io.Serializable;\r
24 import java.io.UnsupportedEncodingException;\r
25 import java.net.URLEncoder;\r
26 import java.util.ArrayList;\r
27 import java.util.HashSet;\r
28 import java.util.List;\r
29 import java.util.Set;\r
30 \r
31 import javax.persistence.CascadeType;\r
32 import javax.persistence.Column;\r
33 import javax.persistence.Embedded;\r
34 import javax.persistence.Entity;\r
35 import javax.persistence.GeneratedValue;\r
36 import javax.persistence.Id;\r
37 import javax.persistence.JoinColumn;\r
38 import javax.persistence.ManyToOne;\r
39 import javax.persistence.OneToMany;\r
40 import javax.persistence.OrderBy;\r
41 import javax.persistence.Table;\r
42 import javax.persistence.UniqueConstraint;\r
43 import javax.persistence.Version;\r
44 \r
45 import org.hibernate.annotations.Cache;\r
46 import org.hibernate.annotations.CacheConcurrencyStrategy;\r
47 \r
48 /**\r
49  * The immutable part of the structure of a file on the GSS service.\r
50  */\r
51 @Entity\r
52 @Table(name="fileheader", uniqueConstraints=@UniqueConstraint(columnNames={"folder_id", "name"}))\r
53 @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)\r
54 public final class FileHeader  implements Serializable{\r
55 \r
56         /**\r
57          * The persistence ID of the object.\r
58          */\r
59         @Id\r
60         @GeneratedValue\r
61         private Long id;\r
62 \r
63         /**\r
64          * Version field for optimistic locking.\r
65          */\r
66         @SuppressWarnings("unused")\r
67         @Version\r
68         private int version;\r
69 \r
70         /**\r
71          * The audit information.\r
72          */\r
73         @Embedded\r
74         private AuditInfo auditInfo;\r
75 \r
76         /**\r
77          * The icon filename.\r
78          */\r
79         private String icon;\r
80 \r
81         /**\r
82          * The file name.\r
83          */\r
84         @Column(name="name")\r
85         private String name;\r
86 \r
87         /**\r
88          * The parent folder of this file.\r
89          */\r
90         @ManyToOne(optional=false)\r
91         @JoinColumn(name="folder_id", nullable=false)\r
92         private Folder folder;\r
93 \r
94         /**\r
95          * Is this a versioned file?\r
96          */\r
97         private boolean versioned = false;\r
98         /**\r
99          * Is this file temporarily deleted?\r
100          * XXX: the columnDefinition is postgres specific, if deployment database is changed this shall be changed too\r
101          */\r
102         @Column(columnDefinition=" boolean DEFAULT false")\r
103         private boolean deleted=false;\r
104 \r
105         /**\r
106          * Can this file be read by anyone?\r
107          * XXX: the columnDefinition is postgres specific, if deployment database is changed this shall be changed too\r
108          */\r
109         @Column(columnDefinition=" boolean DEFAULT false")\r
110         private boolean readForAll=false;\r
111 \r
112         /**\r
113          * The owner of this file.\r
114          */\r
115         @ManyToOne(optional=false)\r
116         @JoinColumn(nullable=false)\r
117         private User owner;\r
118 \r
119         /**\r
120          * The bodies of this file. (A single one if not versioned.) A List so we\r
121          * can keep order.\r
122          */\r
123         @OneToMany(cascade = CascadeType.ALL, mappedBy = "header")\r
124         @OrderBy("version")\r
125         private List<FileBody> bodies = new ArrayList<FileBody>();\r
126 \r
127         /**\r
128          * The current (most recent) body of this file. The single one if not\r
129          * versioned.\r
130          */\r
131         @ManyToOne\r
132         private FileBody currentBody;\r
133 \r
134         /**\r
135          * The list of all tags this file has specified from all Users.\r
136          */\r
137         @OneToMany(cascade = CascadeType.ALL, mappedBy = "file")\r
138         @OrderBy("tag")\r
139         private List<FileTag> fileTags = new ArrayList<FileTag>();\r
140 \r
141         /**\r
142          * Set of Permission objects: The permissions (User and Group) for this\r
143          * FileHeader.\r
144          */\r
145         @OneToMany(cascade = CascadeType.ALL)\r
146         private Set<Permission> permissions = new HashSet<Permission>();\r
147 \r
148         /**\r
149          * Retrieve the ID.\r
150          *\r
151          * @return the ID\r
152          */\r
153         public Long getId() {\r
154                 return id;\r
155         }\r
156 \r
157         /**\r
158          * Retrieve the icon.\r
159          *\r
160          * @return the icon\r
161          */\r
162         public String getIcon() {\r
163                 return icon;\r
164         }\r
165 \r
166         /**\r
167          * Modify the icon.\r
168          *\r
169          * @param newIcon the new icon\r
170          */\r
171         public void setIcon(final String newIcon) {\r
172                 icon = newIcon;\r
173         }\r
174 \r
175         /**\r
176          * Retrieve the name.\r
177          *\r
178          * @return the name\r
179          */\r
180         public String getName() {\r
181                 return name;\r
182         }\r
183 \r
184         /**\r
185          * Modify the name.\r
186          *\r
187          * @param newName the new name\r
188          */\r
189         public void setName(final String newName) {\r
190                 name = newName;\r
191         }\r
192 \r
193         /**\r
194          * Retrieve the folder.\r
195          *\r
196          * @return the folder object\r
197          */\r
198         public Folder getFolder() {\r
199                 return folder;\r
200         }\r
201 \r
202         /**\r
203          * Modify the folder.\r
204          *\r
205          * @param newFolder the new folder\r
206          */\r
207         public void setFolder(final Folder newFolder) {\r
208                 folder = newFolder;\r
209         }\r
210 \r
211         /**\r
212          * Determine whether this file is versioned or not.\r
213          *\r
214          * @return true if this file is versioned\r
215          */\r
216         public boolean isVersioned() {\r
217                 return versioned;\r
218         }\r
219 \r
220         /**\r
221          * Modify the versioning status of this file.\r
222          *\r
223          * @param newStatus the new versioning status\r
224          */\r
225         public void setVersioned(final boolean newStatus) {\r
226                 versioned = newStatus;\r
227         }\r
228 \r
229 \r
230         /**\r
231          * Is this file deleted or not?.\r
232          *\r
233          * @return the deleted\r
234          */\r
235         public boolean isDeleted() {\r
236                 return deleted;\r
237         }\r
238 \r
239 \r
240         /**\r
241          * Set whether this file is deleted .\r
242          *\r
243          * @param newDeleted the deletedFlag to set\r
244          */\r
245         public void setDeleted(boolean newDeleted) {\r
246                 deleted = newDeleted;\r
247         }\r
248 \r
249         /**\r
250          * Retrieve the owner.\r
251          *\r
252          * @return the owner\r
253          */\r
254         public User getOwner() {\r
255                 return owner;\r
256         }\r
257 \r
258         /**\r
259          *Modify the owner.\r
260          *\r
261          * @param newOwner the new owner\r
262          */\r
263         public void setOwner(final User newOwner) {\r
264                 owner = newOwner;\r
265         }\r
266 \r
267         /**\r
268          * Retrieve the list of bodies.\r
269          *\r
270          * @return the list of bodies\r
271          */\r
272         public List<FileBody> getBodies() {\r
273                 return bodies;\r
274         }\r
275 \r
276         /**\r
277          * Replace the list of bodies.\r
278          *\r
279          * @param newBodies the new list of bodies\r
280          */\r
281         public void setBodies(final List<FileBody> newBodies) {\r
282                 bodies = newBodies;\r
283         }\r
284 \r
285         /**\r
286          * Retrieve the current body.\r
287          *\r
288          * @return the current body\r
289          */\r
290         public FileBody getCurrentBody() {\r
291                 return currentBody;\r
292         }\r
293 \r
294         /**\r
295          * Set another body as the current one.\r
296          *\r
297          * @param newCurrentBody the new current body\r
298          */\r
299         public void setCurrentBody(final FileBody newCurrentBody) {\r
300                 currentBody = newCurrentBody;\r
301         }\r
302 \r
303         /**\r
304          * Retrieve the audit info.\r
305          *\r
306          * @return the audit info object\r
307          */\r
308         public AuditInfo getAuditInfo() {\r
309                 return auditInfo;\r
310         }\r
311 \r
312         /**\r
313          * Modify the audit info.\r
314          *\r
315          * @param newAuditInfo the new audit info\r
316          */\r
317         public void setAuditInfo(final AuditInfo newAuditInfo) {\r
318                 auditInfo = newAuditInfo;\r
319         }\r
320 \r
321         /**\r
322          * Retrieve the file tags.\r
323          *\r
324          * @return the list of file tags\r
325          */\r
326         public List<FileTag> getFileTags() {\r
327                 return fileTags;\r
328         }\r
329 \r
330         /**\r
331          * Replace the list of file tags.\r
332          *\r
333          * @param newFileTags the new file tags list\r
334          */\r
335         public void setFileTags(final List<FileTag> newFileTags) {\r
336                 fileTags = newFileTags;\r
337         }\r
338 \r
339         /**\r
340          * Retrieve the set of permissions.\r
341          *\r
342          * @return the permission set\r
343          */\r
344         public Set<Permission> getPermissions() {\r
345                 return permissions;\r
346         }\r
347 \r
348         /**\r
349          * Replace the permission set.\r
350          *\r
351          * @param newPermissions the new permission set\r
352          */\r
353         public void setPermissions(final Set<Permission> newPermissions) {\r
354                 permissions = newPermissions;\r
355         }\r
356 \r
357         /**\r
358          * Add a body to list of bodies.\r
359          *\r
360          * @param body InfoItemBody The body to add.\r
361          */\r
362         public void addBody(final FileBody body) {\r
363                 if (body == null)\r
364                         throw new IllegalArgumentException("Can't add a null FileBody.");\r
365                 // Remove from old header\r
366                 if (body.getHeader() != null)\r
367                         throw new IllegalArgumentException("Trying to add a FileBody that already belongs to a FileHeader.");\r
368 \r
369                 // Set child in parent\r
370                 getBodies().add(body);\r
371                 // Set parent in child\r
372                 body.setHeader(this);\r
373 \r
374                 // Update version number\r
375                 if (currentBody == null)\r
376                         body.setVersion(1);\r
377                 else\r
378                         body.setVersion(currentBody.getVersion() + 1);\r
379 \r
380                 currentBody = body;\r
381         }\r
382 \r
383 \r
384         /**\r
385          * Retrieve the readForAll.\r
386          *\r
387          * @return the readForAll\r
388          */\r
389         public boolean isReadForAll() {\r
390                 return readForAll;\r
391         }\r
392 \r
393 \r
394         /**\r
395          * Modify the readForAll.\r
396          *\r
397          * @param readForAll the readForAll to set\r
398          */\r
399         public void setReadForAll(boolean readForAll) {\r
400                 this.readForAll = readForAll;\r
401         }\r
402 \r
403         /**\r
404          * Returns current body version number formatted for display. Returns "-"\r
405          * for non-versioned file.\r
406          *\r
407          * @return the current version\r
408          */\r
409         public String getCurrentVersionString() {\r
410                 if (isVersioned())\r
411                         return String.valueOf(currentBody.getVersion());\r
412                 return "-";\r
413         }\r
414 \r
415         /**\r
416          * Adds a permission to this FileHeader.\r
417          *\r
418          * @param permission Permission to add\r
419          * @throws IllegalArgumentException if permission is null\r
420          */\r
421         public void addPermission(final Permission permission) {\r
422                 if (permission == null)\r
423                         throw new IllegalArgumentException("Can't add a null Permission.");\r
424                 getPermissions().add(permission);\r
425         }\r
426 \r
427         /**\r
428          * Constructs and returns a DTO for this instance for use by remote clients\r
429          *\r
430          * @return FileHeaderDTO\r
431          */\r
432         public FileHeaderDTO getDTO() {\r
433                 final FileHeaderDTO f = new FileHeaderDTO();\r
434                 f.setId(id);\r
435                 f.setName(name);\r
436                 f.setPath(getPath());\r
437                 f.setFolder(folder.getDTO());\r
438                 f.setVersioned(versioned);\r
439                 f.setVersion(currentBody.getVersion());\r
440                 f.setOwner(owner.getDTO());\r
441                 f.setFileSize(currentBody.getFileSize());\r
442                 f.setOriginalFilename(currentBody.getOriginalFilename());\r
443                 f.setOriginalFilenameEncoded(currentBody.getOriginalFilenameEncoded());\r
444                 f.setMimeType(currentBody.getMimeType());\r
445                 f.setDeleted(deleted);\r
446                 f.setReadForAll(readForAll);\r
447                 List<String> tags = new ArrayList<String>();\r
448                 for (FileTag tag : fileTags)\r
449                         tags.add(tag.getTag());\r
450                 f.setTags(tags);\r
451                 f.setAuditInfo(auditInfo.getDTO());\r
452                 return f;\r
453         }\r
454 \r
455         /**\r
456          * Checks if the specified user has permission to delete this file, by\r
457          * checking if the user has write permission to this object.\r
458          *\r
459          * @param user the specified User\r
460          * @return true if the user has permission to delete the file, false\r
461          *         otherwise\r
462          */\r
463         public boolean hasDeletePermission(final User user) {\r
464                 if (hasWritePermission(user))\r
465                         return true;\r
466                 return false;\r
467         }\r
468 \r
469         /**\r
470          * Checks if the specified user has permission to modify this file.\r
471          *\r
472          * @param user the specified User\r
473          * @return true if the user has permission to modify the file, false\r
474          *         otherwise\r
475          */\r
476         public boolean hasWritePermission(final User user) {\r
477                 for (final Permission p : permissions)\r
478                         if (p.getUser() != null) {\r
479                                 if (p.getUser().equals(user) && p.getWrite())\r
480                                         return true;\r
481                         } else if (p.getGroup().contains(user) && p.getWrite())\r
482                                 return true;\r
483                 return false;\r
484         }\r
485 \r
486         /**\r
487          * Checks if the specified user has permission to read this file.\r
488          *\r
489          * @param user the specified User\r
490          * @return true if the user has permission to read the file, false\r
491          *         otherwise\r
492          */\r
493         public boolean hasReadPermission(final User user) {\r
494                 if(readForAll)\r
495                         return true;\r
496                 for (final Permission p : permissions)\r
497                         if (p.getUser() != null) {\r
498                                 if (p.getUser().equals(user) && p.getRead())\r
499                                         return true;\r
500                         } else if (p.getGroup().contains(user) && p.getRead())\r
501                                 return true;\r
502                 return false;\r
503         }\r
504 \r
505         /**\r
506          * Checks if the specified user has permission to modify the ACL of this file.\r
507          *\r
508          * @param user the specified User\r
509          * @return true if the user has permission to modify the ACL of the file, false\r
510          *         otherwise\r
511          */\r
512         public boolean hasModifyACLPermission(final User user) {\r
513                 for (final Permission p : permissions)\r
514                         if (p.getUser() != null) {\r
515                                 if (p.getUser().equals(user) && p.getModifyACL())\r
516                                         return true;\r
517                         } else if (p.getGroup().contains(user) && p.getModifyACL())\r
518                                 return true;\r
519                 return false;\r
520         }\r
521 \r
522         /**\r
523          * Retrieve the full path of the file, URL-encoded in the form:\r
524          * /parent1/parent2/parent3/name\r
525          *\r
526          * @return the full path from the root of the files namespace\r
527          */\r
528         public String getPath() {\r
529                 try {\r
530                         return folder.getPath() + URLEncoder.encode(name, "UTF-8");\r
531                 } catch (UnsupportedEncodingException e) {\r
532                         throw new RuntimeException(e);\r
533                 }\r
534         }\r
535 \r
536         /**\r
537          * Return the total space occupied by this file in storage\r
538          * (i.e. the total size of all bodies)\r
539          * @return long\r
540          */\r
541         public long getTotalSize() {\r
542                 long total = 0;\r
543                 for (FileBody body: getBodies())\r
544                         total += body.getFileSize();\r
545                 return total;\r
546         }\r
547 }\r
548 \r