- Check when renaming file that name doesn't already exist.
[pithos] / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java
index 56a345a..906eb76 100644 (file)
@@ -63,6 +63,7 @@ import java.util.StringTokenizer;
 
 import javax.ejb.EJB;
 import javax.ejb.EJBException;
+import javax.ejb.EJBTransactionRolledbackException;
 import javax.ejb.Stateless;
 import javax.ejb.TransactionAttribute;
 import javax.ejb.TransactionAttributeType;
@@ -74,10 +75,10 @@ import javax.jms.MessageProducer;
 import javax.jms.Queue;
 import javax.jms.QueueConnectionFactory;
 import javax.jms.Session;
-import javax.jws.WebMethod;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
+import javax.persistence.PersistenceException;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -98,6 +99,7 @@ import org.apache.commons.httpclient.methods.StringRequestEntity;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.hibernate.exception.ConstraintViolationException;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -421,29 +423,38 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public FolderDTO modifyFolder(Long userId, Long folderId, String folderName)
-                       throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException {
+       public FolderDTO updateFolder(Long userId, Long folderId, String folderName,
+                               Set<PermissionDTO> permissions)
+                       throws InsufficientPermissionsException, ObjectNotFoundException,
+                       DuplicateNameException {
 
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
                        throw new ObjectNotFoundException("No folder specified");
-               if (StringUtils.isEmpty(folderName))
-                       throw new ObjectNotFoundException("New folder name is empty");
 
                Folder folder = dao.getEntityById(Folder.class, folderId);
                User user = dao.getEntityById(User.class, userId);
-               if (!folder.hasWritePermission(user))
+               if (folderName != null && !folder.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
+               if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
 
                Folder parent = folder.getParent();
-               if (parent != null)
-                       if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
-                               throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
+               if (folderName != null) {
+                       if (parent != null)
+                               if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
+                                       throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
 
-               // Do the actual modification.
-               folder.setName(folderName);
+                       // Do the actual modification.
+                       folder.setName(folderName);
+               }
+               if (permissions != null)
+                       setFolderPermissions(user, folder, permissions);
+
+               folder.getAuditInfo().setModificationDate(new Date());
+               folder.getAuditInfo().setModifiedBy(user);
                dao.update(folder);
                touchParentFolders(folder, user, new Date());
                return folder.getDTO();
@@ -625,12 +636,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        logger.error("Could not delete file " + filePath);
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#createTag(java.lang.Long,
-        *      java.lang.Long, java.lang.String)
-        */
        public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -648,17 +653,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                touchParentFolders(parent, user, new Date());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserTags(java.lang.Long)
-        */
-       @WebMethod(operationName = "getUserTags")
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
                return dao.getUserTags(userId);
        }
 
        public void updateFile(Long userId, Long fileId, String name,
-                               String tagSet, Date modificationDate)
-                       throws ObjectNotFoundException, InsufficientPermissionsException {
+                               String tagSet, Date modificationDate, Boolean versioned,
+                               Boolean readForAll,     Set<PermissionDTO> permissions)
+                       throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -669,17 +671,29 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("The specified file has no parent folder");
 
                User user = dao.getEntityById(User.class, userId);
-               if (!file.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getId() + " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               // Check permissions for modifying the file metadata.
+               if ((name != null || tagSet != null || modificationDate != null || versioned != null) && !file.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               // Check permissions for making file public.
+               if (readForAll != null && !user.equals(file.getOwner()))
+                               throw new InsufficientPermissionsException("Only the owner can make a file public or not public");
+               // Check permissions for modifying the ACL.
+               if(permissions != null && !permissions.isEmpty() &&     !file.hasModifyACLPermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update the permissions on file " +     file.getName() + "(" + file.getId() + ")");
 
                if (name != null)
+                       // Do plain check for file already exists.
+                       // Extreme concurrency case should be caught by constraint violation later.
+                       if (dao.existsFolderOrFile(parent.getId(), name)) throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
                        file.setName(name);
-               if (modificationDate != null) {
+
+               if (modificationDate != null)
                        file.getAuditInfo().setModificationDate(modificationDate);
-                       file.getAuditInfo().setModifiedBy(user);
-               }
-               List<FileTag> tags = file.getFileTags();
+               else
+                       file.getAuditInfo().setModificationDate(new Date());
+               file.getAuditInfo().setModifiedBy(user);
 
+               List<FileTag> tags = file.getFileTags();
                if (tagSet != null) {
                        Iterator<FileTag> i = tags.iterator();
                        while (i.hasNext()) {
@@ -694,6 +708,31 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        while (st.hasMoreTokens())
                                new FileTag(user, file, st.nextToken().trim());
                }
+               if (versioned != null && !file.isVersioned() == versioned) {
+                       if (file.isVersioned())
+                               removeOldVersions(userId, fileId);
+                       file.setVersioned(versioned);
+               }
+               if (readForAll != null && user.equals(file.getOwner()))
+                       file.setReadForAll(readForAll);
+               if (permissions != null && !permissions.isEmpty())
+                       setFilePermissions(file, permissions);
+
+               /*
+                * Force constraint violation to manifest itself here.
+                * This should cover extreme concurrency cases that the simple check
+                * above hasn't caught.
+                */
+               try {
+                       dao.flush();
+               }
+               catch (EJBTransactionRolledbackException e) {
+                       Throwable cause = e.getCause();
+                       if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
+                               throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
+                       throw e;
+               }
+
                touchParentFolders(parent, user, new Date());
 
                // Re-index the file if it was modified.
@@ -1405,19 +1444,17 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#setFolderPermissions(java.lang.Long, java.lang.Long, java.util.Set)
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * folder.
+        *
+        * @param user
+        * @param folder
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
         */
-       @Override
-       public void setFolderPermissions(Long userId, Long folderId, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               User user = dao.getEntityById(User.class, userId);
-               Folder folder = dao.getEntityById(Folder.class, folderId);
-               if(!folder.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
+       private void setFolderPermissions(User user, Folder folder, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
                // Delete previous entries
                for (Permission perm: folder.getPermissions())
                        dao.delete(perm);
@@ -1430,10 +1467,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        folder.addPermission(getPermission(dto));
                }
                dao.update(folder);
-               for (FileHeader fh : folder.getFiles())
-                       setFilePermissions(userId, fh.getId(), fh.isReadForAll(), permissions);
+               for (FileHeader file : folder.getFiles()) {
+                       setFilePermissions(file, permissions);
+                       Date now = new Date();
+                       file.getAuditInfo().setModificationDate(now);
+                       file.getAuditInfo().setModifiedBy(user);
+               }
                for (Folder sub : folder.getSubfolders())
-                       setFolderPermissions(userId, sub.getId(), permissions);
+                       setFolderPermissions(user, sub, permissions);
        }
 
        private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
@@ -1531,9 +1572,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersSharingFoldersForUser(java.lang.Long)
-        */
        @Override
        public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
                List<User> users = dao.getUsersSharingFoldersForUser(userId);
@@ -1547,9 +1585,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return res;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFilePermissions(java.lang.Long, java.lang.Long)
-        */
        @Override
        public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
@@ -1572,43 +1607,32 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return result;
        }
 
-       @Override
-       public void setFilePermissions(Long userId, Long fileId, Boolean readForAll, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-               if(!file.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-
-               if (readForAll != null)
-                       if (user.equals(file.getOwner()))
-                               file.setReadForAll(readForAll);
-                       else
-                               throw new InsufficientPermissionsException("Only the owner can change the read-for-all flag");
-
-               // Update the file if there was a change.
-               if (readForAll != null || permissions != null && !permissions.isEmpty()) {
-                       if (permissions != null && !permissions.isEmpty()) {
-                               // Delete previous entries
-                               for (Permission perm: file.getPermissions())
-                                       dao.delete(perm);
-                               file.getPermissions().clear();
-                               for (PermissionDTO dto : permissions) {
-                                       if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
-                                               throw new InsufficientPermissionsException("Can't remove permissions from owner");
-                                       // Don't include 'empty' permission
-                                       if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
-                                       file.addPermission(getPermission(dto));
-                               }
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * file. This method sets the modification date/user attributes to the
+        * current values as a side effect.
+        *
+        * @param file
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
+        */
+       private void setFilePermissions(FileHeader file,
+                               Set<PermissionDTO> permissions)
+                       throws ObjectNotFoundException, InsufficientPermissionsException {
+               if (permissions != null && !permissions.isEmpty()) {
+                       // Delete previous entries.
+                       for (Permission perm: file.getPermissions())
+                               dao.delete(perm);
+                       file.getPermissions().clear();
+                       for (PermissionDTO dto : permissions) {
+                               if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
+                                       throw new InsufficientPermissionsException("Can't remove permissions from owner");
+                               // Don't include 'empty' permission.
+                               if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
+                               file.addPermission(getPermission(dto));
                        }
-
-                       dao.update(file);
-                       Folder parent = file.getFolder();
-                       touchParentFolders(parent, user, new Date());
+                       dao.flush();
                }
        }
 
@@ -2052,28 +2076,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                touchParentFolders(parent, user, new Date());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#toggleFileVersioning(java.lang.Long, java.lang.Long, boolean)
-        */
-       @Override
-       public void toggleFileVersioning(Long userId, Long fileId, boolean versioned) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No file specified");
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader header = dao.getEntityById(FileHeader.class, fileId);
-               if(!header.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               if(!header.isVersioned() == versioned){
-                       if(header.isVersioned())
-                               removeOldVersions(userId, fileId);
-                       header.setVersioned(versioned);
-                       Folder parent = header.getFolder();
-                       touchParentFolders(parent, user, new Date());
-               }
-       }
-
        /**
         * Gets the quota left for specified userId
         * @param userId