X-Git-Url: https://code.grnet.gr/git/pithos/blobdiff_plain/4684df80be00b96e2e22a5b9184e63d0827ff895..376d0ebfee4e3cf72a19cbc493a5ec1b0db1da70:/src/gr/ebs/gss/server/ejb/ExternalAPIBean.java diff --git a/src/gr/ebs/gss/server/ejb/ExternalAPIBean.java b/src/gr/ebs/gss/server/ejb/ExternalAPIBean.java index ea6f3be..6014525 100644 --- a/src/gr/ebs/gss/server/ejb/ExternalAPIBean.java +++ b/src/gr/ebs/gss/server/ejb/ExternalAPIBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd. + * Copyright 2007, 2008, 2009, 2010 Electronic Business Systems Ltd. * * This file is part of GSS. * @@ -22,6 +22,7 @@ import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfigu import gr.ebs.gss.client.exceptions.DuplicateNameException; import gr.ebs.gss.client.exceptions.GSSIOException; import gr.ebs.gss.client.exceptions.InsufficientPermissionsException; +import gr.ebs.gss.client.exceptions.InvitationUsedException; import gr.ebs.gss.client.exceptions.ObjectNotFoundException; import gr.ebs.gss.client.exceptions.QuotaExceededException; import gr.ebs.gss.server.domain.AuditInfo; @@ -31,9 +32,11 @@ import gr.ebs.gss.server.domain.FileTag; import gr.ebs.gss.server.domain.FileUploadStatus; import gr.ebs.gss.server.domain.Folder; import gr.ebs.gss.server.domain.Group; +import gr.ebs.gss.server.domain.Invitation; import gr.ebs.gss.server.domain.Nonce; import gr.ebs.gss.server.domain.Permission; import gr.ebs.gss.server.domain.User; +import gr.ebs.gss.server.domain.UserClass; import gr.ebs.gss.server.domain.dto.FileBodyDTO; import gr.ebs.gss.server.domain.dto.FileHeaderDTO; import gr.ebs.gss.server.domain.dto.FolderDTO; @@ -48,8 +51,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.StringWriter; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -63,6 +66,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,35 +78,30 @@ 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.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.NameValuePair; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.StringRequestEntity; +import javax.persistence.PersistenceException; + import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer; +import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrInputDocument; +import org.hibernate.exception.ConstraintViolationException; + +import com.novell.ldap.LDAPAttribute; +import com.novell.ldap.LDAPAttributeSet; +import com.novell.ldap.LDAPConnection; +import com.novell.ldap.LDAPEntry; +import com.novell.ldap.LDAPException; /** * The concrete implementation of the ExternalAPI interface. @@ -139,6 +138,20 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { */ private static Random random = new Random(); + /** + * Mark the folder and all of its parent folders as modified from the specified user. + */ + private void touchParentFolders(Folder folder, User user, Date date) { + Folder f = folder; + while (f != null) { + AuditInfo ai = f.getAuditInfo(); + ai.setModifiedBy(user); + ai.setModificationDate(date); + f.setAuditInfo(ai); + f = f.getParent(); + } + } + @Override public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException { if (userId == null) @@ -147,11 +160,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return folder.getDTO(); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolder(java.lang.Long) - */ + @Override public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -165,27 +174,19 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return folder.getDTO(); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUser(java.lang.Long) - */ + @Override public User getUser(Long userId) throws ObjectNotFoundException { if (userId == null) throw new ObjectNotFoundException("No user specified"); return dao.getEntityById(User.class, userId); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserDTO(java.lang.Long) - */ + @Override public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException { return getUser(userId).getDTO(); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroup(java.lang.Long) - */ + @Override public GroupDTO getGroup(final Long groupId) throws ObjectNotFoundException { if (groupId == null) throw new ObjectNotFoundException("No group specified"); @@ -207,11 +208,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { throw new ObjectNotFoundException("Group " + name + " not found"); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroups(java.lang.Long) - */ + @Override public List getGroups(final Long userId) throws ObjectNotFoundException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -242,12 +239,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return result; } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsers(java.lang.Long, - * java.lang.Long) - */ + @Override public List getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException { // Validate. if (userId == null) @@ -264,7 +256,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } @Override - public void createFolder(Long userId, Long parentId, String name) + public FolderDTO createFolder(Long userId, Long parentId, String name) throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException { // Validate. if (userId == null) @@ -291,7 +283,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { " to write to this folder"); // Do the actual work. - createFolder(name, parent, creator); + return createFolder(name, parent, creator); } /** @@ -300,8 +292,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { * @param name * @param parent * @param creator + * @return the new folder */ - private void createFolder(String name, Folder parent, User creator) { + private FolderDTO createFolder(String name, Folder parent, User creator) { Folder folder = new Folder(); folder.setName(name); if (parent != null) { @@ -317,6 +310,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { auditInfo.setModifiedBy(creator); auditInfo.setModificationDate(now); folder.setAuditInfo(auditInfo); + touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate()); if (parent != null) for (Permission p : parent.getPermissions()) { @@ -336,16 +330,15 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { permission.setModifyACL(true); folder.addPermission(permission); } + + if(parent != null) + folder.setReadForAll(parent.isReadForAll()); + dao.create(folder); + return folder.getDTO(); } - /* - * Deletes the given folder and all its subfolders and files - * Only the permissions for top folder are checked - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFolder(java.lang.Long, - * java.lang.Long) - */ + @Override public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException { // Validate. if (userId == null) @@ -366,6 +359,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { removeSubfolderFiles(folder); parent.removeSubfolder(folder); dao.delete(folder); + touchParentFolders(parent, user, new Date()); } /** @@ -386,6 +380,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } } + @Override @SuppressWarnings("unchecked") public List getSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException { @@ -406,44 +401,57 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } @Override - public void modifyFolder(Long userId, Long folderId, String folderName) - throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException { + public FolderDTO updateFolder(Long userId, Long folderId, String folderName, + Boolean readForAll, + Set 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"); + // Check permissions for making file public. + if (readForAll != null && !user.equals(folder.getOwner())) + throw new InsufficientPermissionsException("Only the owner can make a folder public or not public"); 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); + if (readForAll != null) + setFolderReadForAll(user, folder, readForAll); + folder.getAuditInfo().setModificationDate(new Date()); + folder.getAuditInfo().setModifiedBy(user); dao.update(folder); + touchParentFolders(folder, user, new Date()); + return folder.getDTO(); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#createGroup(java.lang.Long, - * java.lang.String) - */ + @Override public void createGroup(final Long userId, final String name) throws ObjectNotFoundException, DuplicateNameException { // Validate. if (userId == null) throw new ObjectNotFoundException("No user specified"); if (StringUtils.isEmpty(name)) throw new ObjectNotFoundException("New group name is empty"); + if (name.indexOf('/')>=0) + throw new IllegalArgumentException("Character '/' is not allowed in group name"); if (dao.existsGroup(userId, name)) throw new DuplicateNameException("A group with the name '" + name + "' already exists"); @@ -455,12 +463,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { owner.createGroup(name); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteGroup(java.lang.Long, - * java.lang.Long) - */ + @Override public void deleteGroup(final Long userId, final Long groupId) throws ObjectNotFoundException, InsufficientPermissionsException { // Validate. if (userId == null) @@ -471,17 +474,23 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { // Do the actual work. final User owner = dao.getEntityById(User.class, userId); final Group group = dao.getEntityById(Group.class, groupId); + final Date now = new Date(); // Only delete the group if actually owned by the user. if (group.getOwner().equals(owner)) { List folders = dao.getFoldersPermittedForGroup(userId, groupId); for (Folder f : folders){ f.getPermissions().removeAll(group.getPermissions()); - for(FileHeader file : f.getFiles()) + touchFolder(f, owner, now); + for(FileHeader file : f.getFiles()){ file.getPermissions().removeAll(group.getPermissions()); + touchFile(file, owner, now); + } } - List files = dao.getSharedFilesNotInSharedFolders(userId); - for(FileHeader h : files) + List files = dao.getFilesPermittedForGroup(userId, groupId); + for(FileHeader h : files){ h.getPermissions().removeAll(group.getPermissions()); + touchFile(h, owner, now); + } owner.removeSpecifiedGroup(group); dao.delete(group); } @@ -505,6 +514,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { /* (non-Javadoc) * @see gr.ebs.gss.server.ejb.ExternalAPIRemote#indexFile(java.lang.Long, boolean) */ + @Override public void indexFile(Long fileId, boolean delete) { Connection qConn = null; Session session = null; @@ -570,12 +580,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return secondFolder + File.separator + filename; } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFile(java.lang.Long, - * java.lang.Long) - */ + @Override public void deleteFile(final Long userId, final Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException { // Validate. if (userId == null) @@ -594,23 +599,20 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { for (final FileBody body : file.getBodies()) deleteActualFile(body.getStoredFilePath()); dao.delete(file); + touchParentFolders(parent, user, new Date()); indexFile(fileId, true); } - private void deleteActualFile(String filePath) { - if (filePath == null) + @Override + public void deleteActualFile(String path) { + if (path == null) return; - File file = new File(filePath); + File file = new File(path); if (!file.delete()) - logger.error("Could not delete file " + filePath); + logger.error("Could not delete file " + path); } - /* - * (non-Javadoc) - * - * @see gr.ebs.gss.server.ejb.ExternalAPI#createTag(java.lang.Long, - * java.lang.Long, java.lang.String) - */ + @Override public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -621,34 +623,57 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { final User user = dao.getEntityById(User.class, userId); final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId); + final Folder parent = fh.getFolder(); + if (parent == null) + throw new ObjectNotFoundException("The specified file has no parent folder"); user.addTag(fh, tag); + touchParentFolders(parent, user, new Date()); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserTags(java.lang.Long) - */ - @WebMethod(operationName = "getUserTags") + @Override public Set getUserTags(final Long userId) throws ObjectNotFoundException { return dao.getUserTags(userId); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFile(java.lang.Long, java.lang.Long, java.lang.String, java.util.Set) - */ - public void updateFile(Long userId, Long fileId, String name, String tagSet) throws ObjectNotFoundException, InsufficientPermissionsException { + @Override + public void updateFile(Long userId, Long fileId, String name, + String tagSet, Date modificationDate, Boolean versioned, + Boolean readForAll, Set permissions) + throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) throw new ObjectNotFoundException("No user specified"); if (fileId == null) throw new ObjectNotFoundException("No file specified"); FileHeader file = dao.getEntityById(FileHeader.class, fileId); - User user = dao.getEntityById(User.class, userId); - if (!file.hasWritePermission(user)) - throw new InsufficientPermissionsException("User " + user.getId() + " cannot update file " + file.getName() + "(" + file.getId() + ")"); + final Folder parent = file.getFolder(); + if (parent == null) + throw new ObjectNotFoundException("The specified file has no parent folder"); - if (name != null) + User user = dao.getEntityById(User.class, userId); + // 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); - List tags = file.getFileTags(); + } + + if (modificationDate != null) + file.getAuditInfo().setModificationDate(modificationDate); + else + file.getAuditInfo().setModificationDate(new Date()); + file.getAuditInfo().setModifiedBy(user); + List tags = file.getFileTags(); if (tagSet != null) { Iterator i = tags.iterator(); while (i.hasNext()) { @@ -663,6 +688,32 @@ 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. if (name != null || tagSet != null) @@ -696,6 +747,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { /* (non-Javadoc) * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long) */ + @Override public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -721,9 +773,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getFile(java.lang.Long, java.lang.Long) - */ + @Override public FileHeaderDTO getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -797,7 +847,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { * Retrieve a file for the specified user that has the specified name and * its parent folder has id equal to folderId. * - * @param userId the ID of the current user * @param folderId the ID of the parent folder * @param name the name of the requested file * @return the file found @@ -1078,6 +1127,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { file.setDeleted(true); dao.update(file); + touchParentFolders(parent, user, new Date()); } @Override @@ -1110,6 +1160,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { throw new ObjectNotFoundException("No destination file name specified"); FileHeader file = dao.getEntityById(FileHeader.class, fileId); + Folder source = file.getFolder(); Folder destination = dao.getEntityById(Folder.class, destId); User owner = dao.getEntityById(User.class, userId); @@ -1143,10 +1194,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } // move the file to the destination folder file.setFolder(destination); + touchParentFolders(source, owner, new Date()); + touchParentFolders(destination, owner, new Date()); } @Override - public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException { + public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException { if (userId == null) throw new ObjectNotFoundException("No user specified"); if (ownerId == null) @@ -1164,16 +1217,62 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } @Override - public void moveFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException { - // TODO Simple Move and delete of original folder, in production - // scenario we must first check individual files and folders permissions - copyFolderStructure(userId, folderId, destId, destName); - deleteFolder(userId, folderId); + public void moveFolder(Long userId, Long folderId, Long destId, String destName) + throws ObjectNotFoundException, InsufficientPermissionsException, + QuotaExceededException { + Folder source = dao.getEntityById(Folder.class, folderId); + Folder destination = dao.getEntityById(Folder.class, destId); + User user = dao.getEntityById(User.class, userId); + User sourceOwner = source.getOwner(); + User destinationOwner = destination.getOwner(); + // Do not move trashed folders and contents. + if (source.isDeleted()) + return; + // Check permissions. + if (!destination.hasWritePermission(user) + || !source.hasReadPermission(user) + || !source.hasWritePermission(user)) + throw new InsufficientPermissionsException("You don't have the " + + "necessary permissions"); + // Use the same timestamp for all subsequent modifications to make + // changes appear simultaneous. + Date now = new Date(); + // If source and destination are not in the same user's namespace, + // change owners and check quota. + if (!sourceOwner.equals(destinationOwner)) { + changeOwner(source, destinationOwner, user, now); + if (getQuotaLeft(destinationOwner.getId()) < 0) + throw new QuotaExceededException("Not enough free space " + + "available in destination folder"); + } + // Perform the move. + Folder oldParent = source.getParent(); + oldParent.removeSubfolder(source); + destination.addSubfolder(source); + // Mark the former parent and destination trees upwards as modified. + touchParentFolders(oldParent, user, now); + touchParentFolders(source, user, now); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getDeletedFiles(java.lang.Long) + /** + * Recursively change the owner of the specified folder and all of its + * contents to the specified owner. Also mark them all as modified with the + * specified modifier and modificationDate. */ + private void changeOwner(Folder folder, User owner, User modifier, Date modificationDate) { + for (FileHeader file: folder.getFiles()) { + file.setOwner(owner); + file.getAuditInfo().setModificationDate(modificationDate); + file.getAuditInfo().setModifiedBy(modifier); + } + for (Folder sub: folder.getSubfolders()) + changeOwner(sub, owner, modifier, modificationDate); + folder.setOwner(owner); + folder.getAuditInfo().setModificationDate(modificationDate); + folder.getAuditInfo().setModifiedBy(modifier); + } + + @Override public List getDeletedFiles(Long userId) throws ObjectNotFoundException { // Validate. if (userId == null) @@ -1207,6 +1306,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { file.setDeleted(false); dao.update(file); + touchParentFolders(parent, user, new Date()); } @Override @@ -1221,6 +1321,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { throw new InsufficientPermissionsException("You don't have the necessary permissions"); folder.setDeleted(true); dao.update(folder); + touchParentFolders(folder, user, new Date()); for (FileHeader file : folder.getFiles()) moveFileToTrash(userId, file.getId()); for (Folder subFolder : folder.getSubfolders()) @@ -1246,6 +1347,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { for (Folder subFolder : folder.getSubfolders()) removeFolderFromTrash(userId, subFolder.getId()); dao.update(folder); + touchParentFolders(folder, user, new Date()); } @Override @@ -1278,7 +1380,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } @Override - public User createUser(String username, String name, String mail) throws ObjectNotFoundException { + public User createUser(String username, String name, String mail, + String idp, String idpid) throws ObjectNotFoundException { if (username == null) throw new ObjectNotFoundException("No username specified"); if (name == null) @@ -1288,13 +1391,17 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { user.setUsername(username); user.setName(name); user.setEmail(mail); + user.setIdentityProvider(idp); + user.setIdentityProviderId(idpid); Date now = new Date(); AuditInfo auditInfo = new AuditInfo(); auditInfo.setCreationDate(now); auditInfo.setModificationDate(now); user.setAuditInfo(auditInfo); + user.setActive(true); user.generateAuthToken(); user.generateWebDAVPassword(); + user.setUserClass(getDefaultUserClass()); dao.create(user); // Make sure we get an ID in the user object. dao.flush(); @@ -1303,6 +1410,29 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return user; } + /** + * Get the default user class, which is the one with the lowest quota. + */ + private UserClass getDefaultUserClass() { + return getUserClasses().get(0); + } + + @Override + public List getUserClasses() { + List classes = dao.getUserClasses(); + // Create a default user class for first-time use. Afterwards, the + // admin should modify or add to the userclass table. + if (classes.size() == 0) { + UserClass defaultClass = new UserClass(); + defaultClass.setName("default"); + Long defaultQuota = getConfiguration().getLong("quota", new Long(52428800L)); + defaultClass.setQuota(defaultQuota); + dao.create(defaultClass); + classes.add(defaultClass); + } + return classes; + } + @Override public User findUserByEmail(String email) { return dao.findUserByEmail(email); @@ -1314,17 +1444,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } @Override - public User updateUser(String username, String name, String mail) throws ObjectNotFoundException { - if (username == null) - throw new ObjectNotFoundException("No username specified"); - - User user = dao.getUser(username); - user.setName(name); - user.setEmail(mail); - return user; - } - - @Override public User findUser(String username) { if (username == null) return null; @@ -1340,9 +1459,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return user; } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolderPermissions(java.lang.Long, java.lang.Long) - */ @Override public Set getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1366,35 +1482,46 @@ 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 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"); - // Delete previous entries - for (Permission perm: folder.getPermissions()) - dao.delete(perm); - folder.getPermissions().clear(); - for (PermissionDTO dto : permissions) { - if (dto.getUser()!=null && dto.getUser().getId().equals(folder.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; - folder.addPermission(getPermission(dto)); + private void setFolderPermissions(User user, Folder folder, Set permissions) throws ObjectNotFoundException, InsufficientPermissionsException { + if (permissions != null && !permissions.isEmpty()) { + User owner = folder.getOwner(); + PermissionDTO ownerPerm = null; + for (PermissionDTO dto : permissions) + if (dto.getUser() != null && dto.getUser().getId().equals(owner.getId())) { + ownerPerm = dto; + break; + } + if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL()) + throw new InsufficientPermissionsException("Can't remove permissions from owner"); + // Delete previous entries + for (Permission perm: folder.getPermissions()) + dao.delete(perm); + folder.getPermissions().clear(); + for (PermissionDTO dto : permissions) { + // Skip 'empty' permission entries. + if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue; + folder.addPermission(getPermission(dto)); + } + dao.update(folder); + 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(user, sub, permissions); } - dao.update(folder); - for (FileHeader fh : folder.getFiles()) - setFilePermissions(userId, fh.getId(), fh.isReadForAll(), permissions); - for (Folder sub : folder.getSubfolders()) - setFolderPermissions(userId, sub.getId(), permissions); } private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException { @@ -1425,9 +1552,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#addUserToGroup(java.lang.Long, java.lang.Long, java.lang.Long) - */ @Override public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException { if (userId == null) @@ -1471,9 +1595,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return result; } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#removeMemberFromGroup(java.lang.Long, java.lang.Long, java.lang.Long) - */ @Override public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1492,9 +1613,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersSharingFoldersForUser(java.lang.Long) - */ @Override public List getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException { List users = dao.getUsersSharingFoldersForUser(userId); @@ -1508,9 +1626,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 getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1533,41 +1648,38 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return result; } - @Override - public void setFilePermissions(Long userId, Long fileId, Boolean readForAll, Set 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 permissions) + throws ObjectNotFoundException, InsufficientPermissionsException { + if (permissions != null && !permissions.isEmpty()) { + PermissionDTO ownerPerm = null; + for (PermissionDTO dto : permissions) + if (dto.getUser() != null && dto.getUser().getId().equals(file.getOwner().getId())) { + ownerPerm = dto; + break; } + if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL()) + throw new InsufficientPermissionsException("Can't remove permissions from owner"); + // Delete previous entries. + for (Permission perm: file.getPermissions()) + dao.delete(perm); + file.getPermissions().clear(); + for (PermissionDTO dto : permissions) { + // Skip 'empty' permission entries. + if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue; + file.addPermission(getPermission(dto)); } - - dao.update(file); + dao.flush(); } } @@ -1604,9 +1716,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return result; } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getSharedFiles(java.lang.Long, java.lang.Long) - */ @Override public List getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException { if (ownerId == null) @@ -1646,9 +1755,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { User user = dao.getEntityById(User.class, userId); Folder folder = dao.getEntityById(Folder.class, folderId); List result = new ArrayList(); - if (folder.isShared(user)) + if (folder.isShared(user) || folder.isReadForAll()) for (Folder f : folder.getSubfolders()) - if (f.isShared(user) && !f.isDeleted()) + if ((f.isShared(user) || f.isReadForAll()) && !f.isDeleted()) result.add(f.getDTO()); return result; } @@ -1675,9 +1784,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#searchFiles(java.lang.Long, java.lang.String) - */ @Override public List searchFiles(Long userId, String query) throws ObjectNotFoundException { if (userId == null) @@ -1695,95 +1801,41 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { /** * Performs the actuals search on the solr server and returns the results * - * We have to use the dismax query type (instead of the - * standard) because it allows for search time field boosting. This is because we can't use indexing - * time field boosting due to the patched rich indexing API that does not allow it - * * @param userId * @param query * @return a List of FileHeader objects */ private List search(Long userId, String query) { + List result = new ArrayList(); try { - HttpClient httpClient = new HttpClient(); - - GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl")); - NameValuePair[] params = {new NameValuePair("qt", "dismax"), - new NameValuePair("q", query), - new NameValuePair("sort", "score desc"), - new NameValuePair("indent", "on")}; - method.setQueryString(params); - int retryCount = 0; - int statusCode = 0; - String response = null; - do { - statusCode = httpClient.executeMethod(method); - logger.debug("HTTP status: " + statusCode); - response = method.getResponseBodyAsString(); - logger.debug(response); - retryCount++; - if (statusCode != 200 && retryCount < 3) - try { - Thread.sleep(3000); //Give Solr a little time to be available - } catch (InterruptedException e) { - } - } while (statusCode != 200 && retryCount < 3); - if (statusCode != 200) - throw new EJBException("Search query return error:\n" + response); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse(method.getResponseBodyAsStream()); - method.releaseConnection(); - - Node root = doc.getElementsByTagName("response").item(0); - Node lst = root.getFirstChild().getNextSibling(); - Node status = lst.getFirstChild().getNextSibling(); - if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") && - status.getTextContent().equals("0")) { - List fileResult = new ArrayList(); - Node result = lst.getNextSibling().getNextSibling(); - NodeList docs = result.getChildNodes(); - User user = getUser(userId); - for (int i=1; i fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException { for(Long l : fileIds){ @@ -1794,9 +1846,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFiles(java.lang.Long, java.util.List, java.lang.Long) - */ @Override public void moveFiles(Long userId, List fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException { for(Long l : fileIds){ @@ -1806,9 +1855,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFiles(java.lang.Long, java.util.List) - */ @Override public void deleteFiles(Long userId, List fileIds) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1830,6 +1876,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { for (final FileBody body : file.getBodies()) filesToRemove.add(body.getStoredFilePath()); dao.delete(file); + touchParentFolders(parent, user, new Date()); } //then remove physical files if everything is ok for(String physicalFileName : filesToRemove) @@ -1840,9 +1887,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFilesToTrash(java.lang.Long, java.util.List) - */ @Override public void moveFilesToTrash(Long userId, List fileIds) throws ObjectNotFoundException, InsufficientPermissionsException { for(Long l : fileIds) @@ -1893,9 +1937,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { user.setNonceExpiryDate(nonceExpiryDate); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserStatistics(java.lang.Long) - */ @Override public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException { if (userId == null) @@ -1910,9 +1951,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return stats; } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#getVersions(java.lang.Long, java.lang.Long) - */ @Override public List getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1929,9 +1967,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return result; } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#removeVersion(java.lang.Long, java.lang.Long, java.lang.Long) - */ @Override public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException { if (userId == null) @@ -1956,6 +1991,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { deleteActualFile(body.getStoredFilePath()); header.getBodies().remove(body); + Folder parent = header.getFolder(); + touchParentFolders(parent, user, new Date()); } @@ -2004,180 +2041,89 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } header.getCurrentBody().setVersion(1); - } - - /* (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 - * @return + * Gets the quota left for specified user ID. */ - private Long getQuotaLeft(Long userId){ + private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{ Long fileSize = dao.getFileSize(userId); Long quota = getQuota(userId); return quota - fileSize; } /** - * Gets the quota for specified userId - * @param userId - * @return + * Gets the quota for specified user ID. */ - private Long getQuota(@SuppressWarnings("unused") Long userId){ - Long quota = getConfiguration().getLong("quota", new Long(52428800L)); - return quota; + private Long getQuota(Long userId) throws ObjectNotFoundException{ + UserClass uc = getUser(userId).getUserClass(); + if (uc == null) + uc = getDefaultUserClass(); + return uc.getQuota(); } - public void rebuildSolrIndex() { - MessageProducer sender = null; - Session session = null; - Connection qConn = null; + @Override + @TransactionAttribute(TransactionAttributeType.NEVER) + public String rebuildSolrIndex() { try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.newDocument(); - Node root = doc.createElement("delete"); - doc.appendChild(root); - Node queryNode = doc.createElement("query"); - root.appendChild(queryNode); - queryNode.appendChild(doc.createTextNode("*:*")); - - TransformerFactory fact = TransformerFactory.newInstance(); - Transformer trans = fact.newTransformer(); - trans.setOutputProperty(OutputKeys.INDENT, "yes"); - StringWriter sw = new StringWriter(); - StreamResult sr = new StreamResult(sw); - DOMSource source = new DOMSource(doc); - trans.transform(source, sr); - logger.debug(sw.toString()); - - HttpClient httpClient = new HttpClient(); - PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl")); - method.setRequestEntity(new StringRequestEntity(sw.toString())); - int retryCount = 0; - int statusCode = 0; - String response = null; - do { - statusCode = httpClient.executeMethod(method); - logger.debug("HTTP status: " + statusCode); - response = method.getResponseBodyAsString(); - logger.debug(response); - retryCount++; - if (statusCode != 200 && retryCount < 3) - try { - Thread.sleep(10000); //Give Solr a little time to be available - } catch (InterruptedException e) { - } - } while (statusCode != 200 && retryCount < 3); - method.releaseConnection(); - if (statusCode != 200) - throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response); - List fileIds = dao.getAllFileIds(); - - Context jndiCtx = new InitialContext(); - ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA"); - Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue"); - qConn = factory.createConnection(); - session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE); - sender = session.createProducer(queue); + CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url")); + solr.deleteByQuery("*:*"); + solr.commit(); + logger.info("Deleted everything in solr"); + List fileIds = dao.getAllFileIds(); + logger.info("Total of " + fileIds.size() + " will be indexed"); + int i = 0; for (Long id : fileIds) { - MapMessage map = session.createMapMessage(); - map.setObject("id", id); - map.setBoolean("delete", false); - sender.send(map); + postFileToSolr(solr, id); + i++; + if (i % 100 == 0) { + solr.commit(); + logger.info("Sent commit to solr at file " + i); + } } - sendOptimize(httpClient, 0); - } catch (DOMException e) { - throw new EJBException(e); - } catch (TransformerConfigurationException e) { - throw new EJBException(e); - } catch (IllegalArgumentException e) { - throw new EJBException(e); - } catch (HttpException e) { - throw new EJBException(e); - } catch (UnsupportedEncodingException e) { - throw new EJBException(e); - } catch (ParserConfigurationException e) { - throw new EJBException(e); - } catch (TransformerException e) { - throw new EJBException(e); + solr.optimize(); + solr.commit(); + logger.info("Finished indexing of " + i + " files"); + return "Finished indexing of " + i + " files"; } catch (IOException e) { throw new EJBException(e); - } catch (NamingException e) { + } catch (SolrServerException e) { throw new EJBException(e); - } catch (JMSException e) { - throw new EJBException(e); - } - finally { - try { - if (sender != null) - sender.close(); - if (session != null) - session.close(); - if (qConn != null) - qConn.close(); - } - catch (JMSException e) { - logger.warn(e); - } } } - /** - * Sends a optimize message to the solr server - * - * @param httpClient - * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive - * calls to stop the recursion - * @throws UnsupportedEncodingException - * @throws IOException - * @throws HttpException - */ - private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException { - PostMethod method = null; + @Override + @TransactionAttribute(TransactionAttributeType.NEVER) + public String refreshSolrIndex() { try { - logger.debug("Optimize retry: " + retryCount); - method = new PostMethod(getConfiguration().getString("solrUpdateUrl")); - method.setRequestEntity(new StringRequestEntity("", "text/xml", "iso8859-1")); - int statusCode = httpClient.executeMethod(method); - logger.debug("HTTP status: " + statusCode); - String response = method.getResponseBodyAsString(); - logger.debug(response); - if (statusCode != 200 && retryCount < 2) { - try { - Thread.sleep(10000); //Give Solr a little time to be available - } catch (InterruptedException e) { - } - sendOptimize(httpClient, retryCount + 1); + CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url")); + + List fileIds = dao.getAllFileIds(); + logger.info("Total of " + fileIds.size() + " will be indexed"); + int i = 0; + for (Long id : fileIds) { + postFileToSolr(solr, id); + i++; } - } - finally { - if (method != null) - method.releaseConnection(); + if (i % 100 == 0) { + solr.commit(); + logger.debug("Sent commit to solr at file " + i); + } + solr.optimize(); + solr.commit(); + logger.info("Finished indexing of " + i + " files"); + return "Finished indexing of " + i + " files"; + } catch (IOException e) { + throw new EJBException(e); + } catch (SolrServerException e) { + throw new EJBException(e); } } + @Override public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath) throws DuplicateNameException, ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException { @@ -2211,6 +2157,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { parent.addFile(file); // set file owner to folder owner file.setOwner(parent.getOwner()); + //set file's readForAll value according to parent folder readForAll value + file.setReadForAll(parent.isReadForAll()); final Date now = new Date(); final AuditInfo auditInfo = new AuditInfo(); @@ -2238,15 +2186,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } catch (FileNotFoundException e) { throw new GSSIOException(e); } + touchParentFolders(parent, owner, new Date()); dao.flush(); indexFile(file.getId(), false); return file.getDTO(); } - /* (non-Javadoc) - * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFileContents(java.lang.Long, java.lang.Long, java.lang.String, java.io.InputStream) - */ + @Override public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException { if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -2276,6 +2223,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } catch (FileNotFoundException e) { throw new GSSIOException(e); } + Folder parent = file.getFolder(); + touchParentFolders(parent, owner, new Date()); indexFile(fileId, false); return file.getDTO(); @@ -2323,13 +2272,13 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { * @param filePath the uploaded file full path * @param header the file header that will be associated with the new body * @param auditInfo the audit info - * @param owner the owner of the file * @throws FileNotFoundException * @throws QuotaExceededException + * @throws ObjectNotFoundException if the owner was not found */ private void createFileBody(String name, String mimeType, long fileSize, String filePath, FileHeader header, AuditInfo auditInfo) - throws FileNotFoundException, QuotaExceededException { + throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException { long currentTotalSize = 0; if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null) @@ -2370,11 +2319,13 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { dao.flush(); header.addBody(body); + header.setAuditInfo(auditInfo); dao.create(body); } + @Override @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException { if (userId == null) @@ -2408,6 +2359,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } + @Override public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{ if (userId == null) @@ -2430,6 +2382,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { } + @Override public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{ if (userId == null) throw new ObjectNotFoundException("No user specified"); @@ -2526,13 +2479,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return true; } - /** - * Reset WebDAV password for given user. - * - * @param userId - * @return the new password - * @throws ObjectNotFoundException - */ @Override public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException { if (userId == null) @@ -2542,4 +2488,193 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote { return user.getWebDAVPassword(); } + @Override + public Invitation findInvite(String code) { + if (code == null) + return null; + return dao.findInvite(code); + } + + @Override + public void createLdapUser(String username, String firstname, String lastname, String email, String password) { + LDAPConnection lc = new LDAPConnection(); + LDAPAttributeSet attributeSet = new LDAPAttributeSet(); + attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass"))); + attributeSet.add(new LDAPAttribute("uid", username)); + attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname})); + attributeSet.add(new LDAPAttribute("sn", lastname)); + attributeSet.add(new LDAPAttribute("givenName", firstname)); + attributeSet.add(new LDAPAttribute("mail", email)); + attributeSet.add(new LDAPAttribute("userPassword", password)); + String dn = "uid=" + username + "," + getConfiguration().getString("baseDn"); + LDAPEntry newEntry = new LDAPEntry(dn, attributeSet); + try { + lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT); + lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"), + getConfiguration().getString("bindPassword").getBytes("UTF8")); + lc.add(newEntry); + logger.info("Successfully added LDAP account: " + dn); + lc.disconnect(); + } catch(LDAPException e) { + throw new RuntimeException(e); + } catch(UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + } + + @Override + public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException { + User user = findUser(username); + if (user == null) + throw new ObjectNotFoundException("The user was not found"); + Invitation invite = findInvite(code); + if (invite.getUser() != null) + throw new InvitationUsedException("This code has already been used"); + invite.setUser(user); + UserClass couponClass = getCouponUserClass(); + user.setUserClass(couponClass); + return couponClass; + } + + @Override + public UserClass getCouponUserClass() { + return dao.findCouponUserClass(); + } + + /** + * Mark the folder as modified from the specified user and change it's modification date. + */ + private void touchFolder(Folder f, User _user, Date now){ + final AuditInfo auditInfo = f.getAuditInfo(); + auditInfo.setModificationDate(now); + auditInfo.setModifiedBy(_user); + f.setAuditInfo(auditInfo); + } + + /** + * Mark the file as modified from the specified user and change it's modification date. + */ + private void touchFile(FileHeader f, User _user, Date now){ + final AuditInfo auditInfo = f.getAuditInfo(); + auditInfo.setModificationDate(now); + auditInfo.setModifiedBy(_user); + f.setAuditInfo(auditInfo); + } + + /** + * Set the provided readForAll as the new readforAll value of the specified + * folder and sub-folders. + * + * @param user + * @param folder + * @param readForAll + * @throws ObjectNotFoundException + * + */ + private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){ + if (readForAll != null && user.equals(folder.getOwner())){ + folder.setReadForAll(readForAll); + dao.update(folder); + for (FileHeader file : folder.getFiles()) + file.setReadForAll(readForAll); + if(readForAll) + //only update subfolders when readforall is true. otherwise all sub-folders stay untouched + for (Folder sub : folder.getSubfolders()) + setFolderReadForAll(user, sub, readForAll); + + } + + } + + @Override + public void postFileToSolr(CommonsHttpSolrServer solr, Long id) { + try { + FileHeader file = dao.getFileForIndexing(id); + FileBody body = file.getCurrentBody(); + String mime = body.getMimeType(); + boolean multipart = true; + if (!mime.equals("application/pdf") + && !mime.equals("text/plain") + && !mime.equals("text/html") + && !mime.endsWith("msword") + && !mime.endsWith("ms-excel") + && !mime.endsWith("powerpoint") + || (body.getFileSize() > getConfiguration().getLong("solrDocumentUploadLimitInKB") * 1024)) + multipart = false; + + if (!multipart) + sendMetaDataOnly(solr, file); + else { + ContentStreamUpdateRequest solrRequest = new ContentStreamUpdateRequest(getConfiguration().getString("solr.rich.update.path")); + solrRequest.setParam("literal.id", file.getId().toString()); + solrRequest.setParam("literal.name", file.getName()); + for (FileTag t : file.getFileTags()) { + solrRequest.getParams().add("literal.tag", t.getTag()); + } + File fsFile = new File(body.getStoredFilePath()); + solrRequest.addFile(fsFile); +// solrRequest.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true); + try { + solr.request(solrRequest); + } + catch (SolrException e) { + logger.warn("File " + id + " failed with " + e.getLocalizedMessage() + ". Retrying without the file"); + //Let 's try without the file + sendMetaDataOnly(solr, file); + } + catch (NullPointerException e) { + logger.warn("File " + id + " failed with " + e.getLocalizedMessage() + ". Retrying without the file"); + //Let 's try without the file + sendMetaDataOnly(solr, file); + } + catch (SolrServerException e) { + logger.warn("File " + id + " failed with " + e.getLocalizedMessage() + ". Retrying without the file"); + //Let 's try without the file + sendMetaDataOnly(solr, file); + } + } + } catch (MalformedURLException e) { + throw new EJBException(e); + } catch (ObjectNotFoundException e) { + logger.error("Indexing of file id " + id + " failed.", e); + } catch (SolrServerException e) { + throw new EJBException(e); + } catch (IOException e) { + throw new EJBException(e); + } + } + + private void sendMetaDataOnly(CommonsHttpSolrServer solr, FileHeader file) throws SolrServerException, IOException { + SolrInputDocument solrDoc = new SolrInputDocument(); + solrDoc.addField("id", file.getId().toString()); + solrDoc.addField("name", file.getName()); + for (FileTag t : file.getFileTags()) { + solrDoc.addField("tag", t.getTag()); + } + solr.add(solrDoc); + } + + private String tokenizeFilename(String filename){ + StringBuffer result = new StringBuffer(); + StringTokenizer tokenizer = new StringTokenizer(filename,"._"); + while(tokenizer.hasMoreTokens()){ + result.append(tokenizer.nextToken()); + result.append(" "); + } + result.append(filename); + return result.toString(); + } + + private String normalizeSearchQuery(String query) { + if (query.contains("*")) + return query.toLowerCase().replace('ά', 'α').replace('έ', 'ε').replace('ί', 'ι').replace('ή', 'η').replace('ύ', 'υ') + .replace('ό', 'ο').replace('ς', 'σ').replace('ώ', 'ω').replace('ϊ', 'ι').replace('ϋ', 'υ'); + else + return query; + } + + private String escapeCharacters(String text) { + return text.replaceAll(":", "\\\\:"); + } }