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