package org.gcube.data.access.storagehub.handlers; import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.lock.LockException; import javax.jcr.version.Version; import org.gcube.common.authorization.library.AuthorizedTasks; import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.Paths; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; import org.gcube.common.storagehub.model.exceptions.ItemLockedException; import org.gcube.common.storagehub.model.exceptions.StorageHubException; import org.gcube.common.storagehub.model.items.AbstractFileItem; import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.TrashItem; import org.gcube.common.storagehub.model.items.nodes.Content; import org.gcube.common.storagehub.model.plugins.FolderManager; import org.gcube.common.storagehub.model.types.ItemAction; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.PathUtil; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter; import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter; import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton public class TrashHandler { private static Logger log = LoggerFactory.getLogger(TrashHandler.class); ExecutorService executor = Executors.newFixedThreadPool(100); @Inject VersionHandler versionHandler; @Inject AuthorizationChecker authChecker; @Inject AccountingHandler accountingHandler; @Inject Item2NodeConverter item2Node; @Inject Node2ItemConverter node2Item; @Inject PathUtil pathUtil; @Inject FolderPluginHandler managerHandler; @Inject FolderPluginHandler folderHandler; public void removeNodes(Session ses, List itemsToDelete) throws RepositoryException, StorageHubException{ log.debug("defnitively removing nodes with ids {}",itemsToDelete); for (Item item: itemsToDelete) { removeNodesInternally(ses, item, false); } } public void removeOnlyNodesContent(Session ses, List itemsToDelete) throws RepositoryException, StorageHubException{ log.debug("defnitively removing nodes with ids {}",itemsToDelete); for (Item item: itemsToDelete) { removeNodesInternally(ses, item, true); } } private void retrieveContentsToDelete(Set itemsToDelete, Item itemToDelete) throws Exception{ if (itemToDelete instanceof AbstractFileItem) { itemsToDelete.add(((AbstractFileItem) itemToDelete)); }else if (itemToDelete instanceof FolderItem) { //only to be sure to not delete shared content if (itemToDelete.isShared()) return; List items = Utils.getItemList((Node) itemToDelete, Excludes.GET_ONLY_CONTENT , null, true, null); for (Item item: items) retrieveContentsToDelete(itemsToDelete, item); } } private void removeNodesInternally(Session ses, Item itemToDelete, boolean onlyContent) throws RepositoryException, StorageHubException { try { Set itemsToDelete = new HashSet<>(); Node nodeToDelete = ses.getNodeByIdentifier(itemToDelete.getId()); if (itemToDelete instanceof TrashItem) { List trashChildren = Utils.getItemList(nodeToDelete, Excludes.GET_ONLY_CONTENT, null, true, null); for (Item itemContentToRetrieve: trashChildren) retrieveContentsToDelete(itemsToDelete, itemContentToRetrieve); } else retrieveContentsToDelete(itemsToDelete, itemToDelete); if (!onlyContent) nodeToDelete.remove(); String ids = itemsToDelete.stream().map((i) -> i.getId()).collect(Collectors.joining(",")); log.debug("content ids to remove are {}",ids); Runnable deleteFromStorageRunnable = AuthorizedTasks.bind(new Runnable() { @Override public void run() { for (AbstractFileItem item: itemsToDelete ) { try { FolderManager manager = folderHandler.getFolderManager(item); manager.getStorageBackend().onDelete(item.getContent()); List versions = versionHandler.getContentVersionHistory((Node)item.getRelatedNode()); for (Version version: versions) { Content content = node2Item.getContent(version); manager.getStorageBackend().onDelete(content); } log.debug("file with id {} correctly removed from storage {}",item.getId(), manager.getClass().getSimpleName()); }catch(Throwable t) { log.warn("error removing file with id {} from storage",item.getId(), t); } } } }); executor.execute(deleteFromStorageRunnable); if (!onlyContent) ses.save(); }catch (LockException e) { throw new ItemLockedException("the selected node or his parent is locked", e); }catch (Exception e) { throw new BackendGenericError("error permanetly deleting items",e); } } public void moveToTrash(Session ses, Node nodeToDelete, Item item, String login) throws RepositoryException, BackendGenericError{ log.debug("moving node {} to trash ",item.getId()); final Node trashFolder = ses.getNode(pathUtil.getTrashPath(login, ses).toPath()); try { ses.getWorkspace().getLockManager().lock(trashFolder.getPath(), true, true, 0,login); ses.getWorkspace().getLockManager().lock(nodeToDelete.getPath(), true, true, 0,login); log.debug("preparing thrash item"); TrashItem trashItem = new TrashItem(); trashItem.setDeletedBy(login); trashItem.setDeletedFrom(nodeToDelete.getParent().getPath()); Calendar now = Calendar.getInstance(); trashItem.setDeletedTime(now); trashItem.setHidden(false); trashItem.setLastAction(ItemAction.CREATED); trashItem.setDescription("trash item of node " + nodeToDelete.getPath()); trashItem.setParentId(trashFolder.getIdentifier()); trashItem.setParentPath(trashFolder.getPath()); //String pathUUid= UUID.randomUUID().toString(); trashItem.setTitle(item.getTitle()); trashItem.setName(item.getId()); trashItem.setOriginalParentId(nodeToDelete.getParent().getIdentifier()); trashItem.setOwner(login); trashItem.setLastModificationTime(item.getLastModificationTime()); trashItem.setLastModifiedBy(item.getLastModifiedBy()); trashItem.setLenght(0); if (item instanceof FolderItem) { trashItem.setFolder(true); }else if (item instanceof AbstractFileItem ) { AbstractFileItem file = (AbstractFileItem) item; if (file.getContent()!=null) { trashItem.setMimeType(file.getContent().getMimeType()); trashItem.setLenght(file.getContent().getSize()); } trashItem.setFolder(false); } log.debug("creating node"); Node newTrashItemNode = item2Node.getNode(trashFolder, trashItem); ses.save(); log.debug("calling jcr move"); ses.getWorkspace().move(nodeToDelete.getPath(), Paths.append(Paths.getPath(newTrashItemNode.getPath()),nodeToDelete.getName()).toPath()); String mimetype = null; if (item instanceof AbstractFileItem) { if (((AbstractFileItem)item).getContent()!=null) mimetype = ((AbstractFileItem) item).getContent().getMimeType(); else log.warn("the AbstractFileItem with id {} has no content (check it!!)", item.getId()); } accountingHandler.createFolderRemoveObj(item.getName(), item.getClass().getSimpleName(), mimetype, ses, login, (Node) item.getRelatedNode(), true); }catch(Throwable t) { log.error("error exceuting move to trash",t); throw new BackendGenericError(t); }finally { ses.getWorkspace().getLockManager().unlock(nodeToDelete.getPath()); ses.getWorkspace().getLockManager().unlock(trashFolder.getPath()); } } public String restoreItem(Session ses, TrashItem item, FolderItem destination, String login) throws RepositoryException, StorageHubException, BackendGenericError{ log.debug("restoring node from trash with user "); //final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); Node destinationNode= null; if (destination==null) { boolean originalParentExists = true; boolean originalParentTrashed = false; Node originalParent = null; try { originalParent = ses.getNodeByIdentifier(item.getOriginalParentId()); }catch (ItemNotFoundException e) { originalParentExists = false; } Item originalParentItem = null; if (originalParentExists) { originalParentItem = node2Item.getItem(originalParent, Excludes.ALL); originalParentTrashed = originalParentItem.isTrashed(); } if(originalParentExists && !originalParentTrashed) { destinationNode = originalParent; }else { String homeWS = pathUtil.getWorkspacePath(login).toPath(); Node node = ses.getNode(homeWS); destinationNode = node; } } else { Node node = (Node)destination.getRelatedNode(); if (!node2Item.checkNodeType(node, FolderItem.class)) throw new InvalidCallParameters("destination Node is not a folder"); destinationNode = node; } authChecker.checkWriteAuthorizationControl(ses, login, destinationNode.getIdentifier(), true ); ses.getWorkspace().getLockManager().lock(destinationNode.getPath(), true, true, 0,login); String newNodePath = null; try { //the real node is a child of the Trash node List items = Utils.getItemList(ses.getNodeByIdentifier(item.getId()), Excludes.ALL, null, false, null); if (items.size()!=1) { log.warn("a problem occurred restoring item from trash"); throw new BackendGenericError("An error occurred on trash item"); } Item itemToMove = items.get(0); item2Node.updateOwnerOnSubTree(ses.getNodeByIdentifier(itemToMove.getId()), login); String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destinationNode, itemToMove.getName()); newNodePath = Paths.append(Paths.getPath(destinationNode.getPath()),uniqueName).toPath(); ses.move(itemToMove.getPath(), newNodePath); Utils.setPropertyOnChangeNode(ses.getNode(newNodePath), login, ItemAction.MOVED); ses.removeItem(item.getPath()); ses.save(); }catch (Exception e) { if (ses.getWorkspace().getLockManager().isLocked(destinationNode.getPath())) ses.getWorkspace().getLockManager().unlock(destinationNode.getPath()); } return ses.getNode(newNodePath).getIdentifier(); } }