diff --git a/pom.xml b/pom.xml index 7642c18..03329c5 100644 --- a/pom.xml +++ b/pom.xml @@ -47,12 +47,12 @@ - + - org.gcube.core - common-smartgears + org.gcube.core + common-smartgears - + org.gcube.common common-authorization @@ -85,17 +85,17 @@ 5.5.6 - - gov.nih.imagej - imagej - 1.47 - - org.slf4j slf4j-api + + org.gcube.common + gxRest + [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT) + + @@ -214,9 +214,21 @@ com.google.guava guava 16.0 - + + org.apache.commons + commons-compress + 1.17 + + + + org.tukaani + xz + 1.5 + + + org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-simple diff --git a/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java b/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java index 33837ec..4e2c10d 100644 --- a/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java +++ b/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java @@ -1,10 +1,9 @@ package org.gcube.data.access.storagehub; -import static org.gcube.common.storagehub.model.NodeConstants.*; -import java.util.Arrays; - +import javax.inject.Inject; import javax.inject.Singleton; import javax.jcr.Node; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.security.AccessControlEntry; import javax.jcr.security.Privilege; @@ -12,70 +11,124 @@ import javax.jcr.security.Privilege; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.acls.AccessType; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; @Singleton public class AuthorizationChecker { - public void checkReadAuthorizationControl(Session session, String id) throws Exception{ + @Inject + Node2ItemConverter node2Item; + + public void checkReadAuthorizationControl(Session session, String id) throws UserNotAuthorizedException , BackendGenericError, RepositoryException{ Node node = session.getNodeByIdentifier(id); - Item item = ItemHandler.getItem(node, Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME)); + String login = AuthorizationProvider.instance.get().getClient().getId(); + + Item item = node2Item.getItem(node, Excludes.ALL); + if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id+": it's not a valid StorageHub node"); + + if (item.isPublicItem()) return; + if (item.isShared()) { - SharedFolder parentShared = retrieveSharedFolderParent(item, session); - if (!parentShared.getUsers().getValues().containsKey(AuthorizationProvider.instance.get().getClient().getId())) - throw new IllegalAccessException("Insufficent Provileges to read node with id "+id); - } else if (!item.getOwner().equals(AuthorizationProvider.instance.get().getClient().getId())) - throw new IllegalAccessException("Insufficent Provileges to read node with id "+id); + SharedFolder parentShared = node2Item.getItem(retrieveSharedFolderParent(node, session), Excludes.EXCLUDE_ACCOUNTING); + if (!parentShared.getUsers().getValues().containsKey(login)) + throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id); + } else if (!item.getOwner().equals(login)) + throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id); } - private SharedFolder retrieveSharedFolderParent(Item item, Session session) throws Exception{ - if (item instanceof SharedFolder) return (SharedFolder)item; + private Node retrieveSharedFolderParent(Node node, Session session) throws BackendGenericError, RepositoryException{ + if (node2Item.checkNodeType(node, SharedFolder.class)) return node; else - return retrieveSharedFolderParent(ItemHandler.getItem(session.getNodeByIdentifier(item.getParentId()), Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME)), session); + return retrieveSharedFolderParent(node.getParent(), session); } - public void checkWriteAuthorizationControl(Session session, String id, boolean isNewItem) throws Exception { + public void checkWriteAuthorizationControl(Session session, String id, boolean isNewItem) throws UserNotAuthorizedException, BackendGenericError, RepositoryException { //in case of newItem the id is the parent otherwise the old node to replace Node node = session.getNodeByIdentifier(id); - Item item = ItemHandler.getItem(node, Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME, METADATA_NAME)); - + Item item = node2Item.getItem(node, Excludes.ALL); + + String login = AuthorizationProvider.instance.get().getClient().getId(); + + if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's not a valid StorageHub node"); + + if (Constants.PROTECTED_FOLDER.contains(item.getName()) || Constants.PROTECTED_FOLDER.contains(item.getTitle())) + throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's a protected folder"); + if (item.isShared()) { - SharedFolder parentShared = retrieveSharedFolderParent(item, session); - JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentShared.getPath()); + Node parentSharedNode = retrieveSharedFolderParent(node, session); + JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentSharedNode.getPath()); AccessControlEntry[] entries = accessControlList.getAccessControlEntries(); //put it in a different method - + SharedFolder parentShared = node2Item.getItem(parentSharedNode, Excludes.EXCLUDE_ACCOUNTING); for (AccessControlEntry entry: entries) { - if (entry.getPrincipal().getName().equals(AuthorizationProvider.instance.get().getClient().getId()) || (parentShared.isVreFolder() && entry.getPrincipal().getName().equals(parentShared.getTitle()))) { + if (entry.getPrincipal().getName().equals(login) || (parentShared.isVreFolder() && entry.getPrincipal().getName().equals(parentShared.getTitle()))) { for (Privilege privilege : entry.getPrivileges()){ AccessType access = AccessType.fromValue(privilege.getName()); if (isNewItem && access!=AccessType.READ_ONLY) return; else if (!isNewItem && - (access==AccessType.ADMINISTRATOR || access==AccessType.WRITE_ALL || (access==AccessType.WRITE_OWNER && item.getOwner().equals(AuthorizationProvider.instance.get().getClient().getId())))) + (access==AccessType.ADMINISTRATOR || access==AccessType.WRITE_ALL || (access==AccessType.WRITE_OWNER && item.getOwner().equals(login)))) return; } - throw new IllegalAccessException("Insufficent Provileges to write node with id "+id); + throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id); } } } else - if(item.getOwner().equals(AuthorizationProvider.instance.get().getClient().getId())) + if(item.getOwner().equals(login)) return; - throw new IllegalAccessException("Insufficent Provileges to write node with id "+id); + throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id); } + + public void checkAdministratorControl(Session session, SharedFolder item) throws UserNotAuthorizedException, BackendGenericError, RepositoryException { + + String login = AuthorizationProvider.instance.get().getClient().getId(); + + if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+item.getId()+": it's not a valid StorageHub node"); + + Node node = session.getNodeByIdentifier(item.getId()); + + if (item.isShared()) { + Node parentSharedNode = retrieveSharedFolderParent(node, session); + JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentSharedNode.getPath()); + AccessControlEntry[] entries = accessControlList.getAccessControlEntries(); + //put it in a different method + + SharedFolder parentShared = node2Item.getItem(parentSharedNode, Excludes.EXCLUDE_ACCOUNTING); + for (AccessControlEntry entry: entries) { + if (entry.getPrincipal().getName().equals(login) || (parentShared.isVreFolder() && entry.getPrincipal().getName().equals(parentShared.getTitle()))) { + for (Privilege privilege : entry.getPrivileges()){ + AccessType access = AccessType.fromValue(privilege.getName()); + if (access==AccessType.ADMINISTRATOR) + return; + + } + throw new UserNotAuthorizedException("The user "+login+" is not an administrator of node with id "+item.getId()); + } + } + + } + + throw new UserNotAuthorizedException("The user "+login+" is not an administrator of node with id "+item.getId()); + + } + + /* private String retrieveOwner(Node node) { Node nodeOwner; diff --git a/src/main/java/org/gcube/data/access/storagehub/Constants.java b/src/main/java/org/gcube/data/access/storagehub/Constants.java index 3640c38..070da7a 100644 --- a/src/main/java/org/gcube/data/access/storagehub/Constants.java +++ b/src/main/java/org/gcube/data/access/storagehub/Constants.java @@ -19,4 +19,6 @@ public class Constants { public static final String ADMIN_PARAM_PWD ="admin-pwd"; public static final List FOLDERS_TO_EXLUDE = Arrays.asList(Constants.VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME); + + public static final List PROTECTED_FOLDER = Arrays.asList(Constants.VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME); } diff --git a/src/main/java/org/gcube/data/access/storagehub/ConstraintChecker.java b/src/main/java/org/gcube/data/access/storagehub/ConstraintChecker.java new file mode 100644 index 0000000..34c61a5 --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/ConstraintChecker.java @@ -0,0 +1,16 @@ +package org.gcube.data.access.storagehub; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ConstraintChecker { + + public boolean isValidName(String name){ + //^ < > ? $ / \ ' " + Pattern p = Pattern.compile("[^a-z0-9 _/-/?/$<>']", Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(name); + boolean b = m.find(); + return !b; + } + +} diff --git a/src/main/java/org/gcube/data/access/storagehub/MultipleOutputStream.java b/src/main/java/org/gcube/data/access/storagehub/MultipleOutputStream.java index e902eb3..9978a83 100644 --- a/src/main/java/org/gcube/data/access/storagehub/MultipleOutputStream.java +++ b/src/main/java/org/gcube/data/access/storagehub/MultipleOutputStream.java @@ -19,16 +19,16 @@ public class MultipleOutputStream { private MyPipedOututStream[] pipedOutStreams; private int index=0; - + public MultipleOutputStream(InputStream is, int number) throws IOException{ this.is = is; - - + + logger.debug("requested {} piped streams ",number); - + pipedInStreams = new MyPipedInputStream[number]; pipedOutStreams = new MyPipedOututStream[number]; - + for (int i =0; i=pipedInStreams.length) return null; return pipedInStreams[index++]; } - + public class MyPipedOututStream extends PipedOutputStream{ diff --git a/src/main/java/org/gcube/data/access/storagehub/Utils.java b/src/main/java/org/gcube/data/access/storagehub/Utils.java index 48db7cf..6dea100 100644 --- a/src/main/java/org/gcube/data/access/storagehub/Utils.java +++ b/src/main/java/org/gcube/data/access/storagehub/Utils.java @@ -1,37 +1,46 @@ package org.gcube.data.access.storagehub; -import static org.gcube.common.storagehub.model.NodeConstants.ACCOUNTING_NAME; -import static org.gcube.common.storagehub.model.NodeConstants.CONTENT_NAME; - import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Calendar; import java.util.Deque; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.version.Version; +import org.apache.commons.io.FilenameUtils; +import org.apache.jackrabbit.util.Text; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +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.items.AbstractFileItem; import org.gcube.common.storagehub.model.items.FolderItem; +import org.gcube.common.storagehub.model.items.GCubeItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; +import org.gcube.common.storagehub.model.types.ItemAction; import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.contentmanager.storageclient.wrapper.AccessType; import org.gcube.contentmanager.storageclient.wrapper.MemoryType; import org.gcube.contentmanager.storageclient.wrapper.StorageClient; import org.gcube.data.access.storagehub.accounting.AccountingHandler; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; +import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +49,7 @@ public class Utils { public final static String SERVICE_NAME = "home-library"; public final static String SERVICE_CLASS = "org.gcube.portlets.user"; private static final String FOLDERS_TYPE = "nthl:workspaceItem"; - + private static final Logger logger = LoggerFactory.getLogger(Utils.class); public static String getSecurePassword(String user) throws Exception { @@ -61,54 +70,48 @@ public class Utils { } return digest; } - - public static long getItemCount(Node parent, boolean showHidden) throws Exception{ - NodeIterator iterator = parent.getNodes(); - long count=0; - while (iterator.hasNext()){ - Node current = iterator.nextNode(); - - if (isToExclude(current, showHidden)) - continue; - - count++; - } - return count; + + public static long getItemCount(Node parent, boolean showHidden, Class nodeType) throws RepositoryException, BackendGenericError{ + return getItemList(parent, Excludes.ALL, null, showHidden, nodeType).size(); } - - - - - - public static List getItemList(Node parent, List excludes, Range range, boolean showHidden) throws Exception{ + + + + + + public static List getItemList(Node parent, List excludes, Range range, boolean showHidden, Class nodeTypeToInclude) throws RepositoryException, BackendGenericError{ List returnList = new ArrayList(); long start = System.currentTimeMillis(); NodeIterator iterator = parent.getNodes(); logger.trace("time to get iterator {}",(System.currentTimeMillis()-start)); + logger.trace("nodeType is {}",nodeTypeToInclude); int count =0; logger.trace("selected range is {}", range); + Node2ItemConverter node2Item= new Node2ItemConverter(); while (iterator.hasNext()){ Node current = iterator.nextNode(); - + if (isToExclude(current, showHidden)) continue; if (range==null || (count>=range.getStart() && returnList.size() getAllNodesForZip(FolderItem directory, Session session, AccountingHandler accountingHandler) throws Exception{ + + public static Deque getAllNodesForZip(FolderItem directory, Session session, AccountingHandler accountingHandler, List excludes) throws RepositoryException, BackendGenericError{ Deque queue = new LinkedList(); Node currentNode = session.getNodeByIdentifier(directory.getId()); queue.push(directory); Deque tempQueue = new LinkedList(); - logger.debug("adding directory {}",directory.getPath()); - for (Item item : Utils.getItemList(currentNode,null, null, false)){ + logger.debug("adding directory {}",currentNode.getPath()); + for (Item item : Utils.getItemList(currentNode,Excludes.GET_ONLY_CONTENT, null, false, null)){ + if (excludes.contains(item.getId())) continue; if (item instanceof FolderItem) - tempQueue.addAll(getAllNodesForZip((FolderItem) item, session, accountingHandler)); + tempQueue.addAll(getAllNodesForZip((FolderItem) item, session, accountingHandler, excludes)); else if (item instanceof AbstractFileItem){ logger.debug("adding file {}",item.getPath()); AbstractFileItem fileItem = (AbstractFileItem) item; @@ -150,31 +154,39 @@ public class Utils { Item item = queue.pop(); if (item instanceof FolderItem) { actualPath = Paths.getPath(item.getPath()); - logger.trace("actualPath is {}",actualPath.toPath()); + logger.debug("actualPath is {}",actualPath.toPath()); String name = Paths.remove(actualPath, originalPath).toPath().replaceFirst("/", ""); - logger.trace("writing dir {}",name); - zos.putNextEntry(new ZipEntry(name)); - zos.closeEntry(); - } else if (item instanceof AbstractFileItem){ - InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(((AbstractFileItem)item).getContent().getStorageId()); - if (streamToWrite == null){ - logger.warn("discarding item {} ",item.getName()); - continue; - } - try(BufferedInputStream is = new BufferedInputStream(streamToWrite)){ - String name = Paths.remove(actualPath, originalPath).toPath()+item.getName().replaceFirst("/", ""); - logger.trace("writing file {}",name); + logger.debug("writing dir {}",name); + if (name.isEmpty()) continue; + try { zos.putNextEntry(new ZipEntry(name)); - copyStream(is, zos); - - }catch (Exception e) { - logger.warn("error writing item {}", item.getName(),e); - } finally{ + }finally { zos.closeEntry(); } + } else if (item instanceof AbstractFileItem){ + try { + InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(((AbstractFileItem)item).getContent().getStorageId()); + if (streamToWrite == null){ + logger.warn("discarding item {} ",item.getName()); + continue; + } + try(BufferedInputStream is = new BufferedInputStream(streamToWrite)){ + String name = (Paths.remove(actualPath, originalPath).toPath()+item.getName()).replaceFirst("/", ""); + logger.debug("writing file {}",name); + zos.putNextEntry(new ZipEntry(name)); + copyStream(is, zos); + }catch (Exception e) { + logger.warn("error writing item {}", item.getName(),e); + } finally{ + zos.closeEntry(); + } + zos.flush(); + }catch (Throwable e) { + logger.warn("error reading content for item {}", item.getPath(),e); + } } } - + zos.close(); } private static void copyStream(InputStream in, OutputStream out) throws IOException { @@ -184,14 +196,127 @@ public class Utils { out.write(buffer, 0, readcount); } } - - public static boolean hasSharedChildren(FolderItem item, Session session) throws Exception{ - Node currentNode = session.getNodeByIdentifier(item.getId()); - for (Item children : Utils.getItemList(currentNode,Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME), null, false)){ - if (children instanceof FolderItem) - return (children instanceof SharedFolder) || hasSharedChildren((FolderItem)children, session); + + + public static boolean hasSharedChildren(Node node) throws RepositoryException, BackendGenericError{ + Node2ItemConverter node2Item = new Node2ItemConverter(); + NodeIterator children = node.getNodes(); + + while (children.hasNext()) { + Node child= children.nextNode(); + if (node2Item.checkNodeType(child, SharedFolder.class)) return true; + if (node2Item.checkNodeType(child, FolderItem.class) && hasSharedChildren(child)) return true; } return false; + } + + public static void getAllContentIds(Session ses, Set idsToDelete, Item itemToDelete, VersionHandler versionHandler) throws Exception{ + if (itemToDelete instanceof AbstractFileItem) { + List versions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(itemToDelete.getId()), ses); + + versions.forEach(v -> { + try { + String storageId =v.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString(); + idsToDelete.add(storageId); + logger.info("retrieved StorageId {} for version {}", storageId, v.getName()); + } catch (Exception e) { + logger.warn("error retreiving sotrageId version for item with id {}",itemToDelete.getId(),e); + } + }); + + idsToDelete.add(((AbstractFileItem) itemToDelete).getContent().getStorageId()); + }else if (itemToDelete instanceof FolderItem) { + List items = Utils.getItemList(ses.getNodeByIdentifier(itemToDelete.getId()), Excludes.GET_ONLY_CONTENT , null, true, null); + for (Item item: items) + getAllContentIds(ses, idsToDelete, item, versionHandler); + + } } + + public static String checkExistanceAndGetUniqueName(Session ses, Node destination, String name) throws BackendGenericError{ + try { + destination.getNode(name); + }catch(PathNotFoundException pnf) { + return Text.escapeIllegalJcrChars(name); + } catch (Exception e) { + throw new BackendGenericError(e); + } + + try { + String filename = FilenameUtils.getBaseName(name); + String ext = FilenameUtils.getExtension(name); + + String nameTocheck = ext.isEmpty()? String.format("%s(*)",filename): String.format("%s(*).%s",filename, ext); + + logger.debug("filename is {}, extension is {} , and name to check is {}", filename, ext, nameTocheck); + + NodeIterator ni = destination.getNodes(nameTocheck); + int maxval = 0; + while (ni.hasNext()) { + Node n = ni.nextNode(); + int actual = Integer.parseInt(n.getName().replaceAll(String.format("%s\\((\\d*)\\).*", filename), "$1")); + if (actual>maxval) + maxval = actual; + } + + + String newName = ext.isEmpty()? String.format("%s(%d)", filename,maxval+1) : String.format("%s(%d).%s", filename,maxval+1, ext) ; + return Text.escapeIllegalJcrChars(newName); + } catch (Exception e) { + throw new BackendGenericError(e); + } + } + + public static Node createFolderInternally(Session ses, Node destinationNode, String name, String description, String login, AccountingHandler accountingHandler) throws BackendGenericError { + + String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destinationNode, name); + + FolderItem item = new FolderItem(); + Calendar now = Calendar.getInstance(); + item.setName(uniqueName); + item.setTitle(uniqueName); + item.setDescription(description); + //item.setCreationTime(now); + item.setHidden(false); + item.setLastAction(ItemAction.CREATED); + item.setLastModificationTime(now); + item.setLastModifiedBy(login); + item.setOwner(login); + item.setPublicItem(false); + + //to inherit hidden property + //item.setHidden(destinationItem.isHidden()); + + Node newNode = new Item2NodeConverter().getNode(ses, destinationNode, item); + accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false); + return newNode; + } + + public static Node createGcubeItemInternally(Session ses, Node destinationNode, String name, String description, String login, GCubeItem gcubeItem, AccountingHandler accountingHandler) throws BackendGenericError { + + Calendar now = Calendar.getInstance(); + gcubeItem.setName(name); + gcubeItem.setTitle(name); + gcubeItem.setDescription(description); + //item.setCreationTime(now); + gcubeItem.setHidden(false); + gcubeItem.setLastAction(ItemAction.CREATED); + gcubeItem.setLastModificationTime(now); + gcubeItem.setLastModifiedBy(login); + gcubeItem.setOwner(login); + //to inherit hidden property + //item.setHidden(destinationItem.isHidden()); + + Node newNode = new Item2NodeConverter().getNode(ses, destinationNode, gcubeItem); + //TODO: accounting for GCUBEITEM + //accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false); + return newNode; + } + + public static void setPropertyOnChangeNode(Node node, String login, ItemAction action) throws RepositoryException { + node.setProperty(NodeProperty.LAST_MODIFIED.toString(), Calendar.getInstance()); + node.setProperty(NodeProperty.LAST_MODIFIED_BY.toString(), login); + node.setProperty(NodeProperty.LAST_ACTION.toString(), action.name()); + } } diff --git a/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java b/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java index 01feb55..b514d8d 100644 --- a/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java @@ -29,6 +29,10 @@ public class AccountingHandler { private static final String ITEM_TYPE = "hl:itemType"; private static final String MIME_TYPE = "hl:mimeType"; private static final String MEMBERS = "hl:members"; + private static final String OLD_ITEM_NAME = "hl:oldItemName"; + private static final String NEW_ITEM_NAME = "hl:newItemName"; + + private static final Logger logger = LoggerFactory.getLogger(AccountingHandler.class); @@ -112,7 +116,7 @@ public class AccountingHandler { } } - public void shareFolder(String title, Set users, Session ses, Node sharedNode, boolean saveHistory ) { + public void createShareFolder(String title, Set users, Session ses, Node sharedNode, boolean saveHistory ) { try { if (!sharedNode.hasNode(NodeProperty.ACCOUNTING.toString())){ @@ -131,4 +135,44 @@ public class AccountingHandler { logger.warn("error trying to retrieve accountign node",e); } } + + public void createUnshareFolder(String title, Session ses, Node sharedNode, boolean saveHistory ) { + try { + + if (!sharedNode.hasNode(NodeProperty.ACCOUNTING.toString())){ + sharedNode.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString()); + } + + Node accountingNodeParent = sharedNode.getNode(NodeProperty.ACCOUNTING.toString()); + Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.SHARE.getNodeTypeDefinition()); + accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId()); + accountingNode.setProperty(DATE, Calendar.getInstance()); + accountingNode.setProperty(ITEM_NAME, title); + + if (saveHistory) ses.save(); + } catch (RepositoryException e) { + logger.warn("error trying to retrieve accountign node",e); + } + } + + public void createRename(String oldTitle, String newTitle, Node node, Session ses, boolean saveHistory ) { + try { + + if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){ + node.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString()); + } + + Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString()); + Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.RENAMING.getNodeTypeDefinition()); + accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId()); + accountingNode.setProperty(DATE, Calendar.getInstance()); + accountingNode.setProperty(OLD_ITEM_NAME, oldTitle); + accountingNode.setProperty(NEW_ITEM_NAME, newTitle); + + if (saveHistory) ses.save(); + } catch (RepositoryException e) { + logger.warn("error trying to retrieve accountign node",e); + } + } + } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/ClassHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/ClassHandler.java index 3fbe144..0b35a7a 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/ClassHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/ClassHandler.java @@ -14,13 +14,21 @@ public class ClassHandler { private static Logger log = LoggerFactory.getLogger(ClassHandler.class); + private static ClassHandler instance = null; + + public static ClassHandler instance() { + if (instance == null) + instance = new ClassHandler(); + return instance; + } + private Reflections reflection = new Reflections(); private Map> classMap = new HashMap>(); private Map, String> typeMap = new HashMap, String>(); - public ClassHandler() { + private ClassHandler() { Set> classesAnnotated = reflection.getTypesAnnotatedWith(RootNode.class); for (Class clazz: classesAnnotated ){ diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java b/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java new file mode 100644 index 0000000..474d662 --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java @@ -0,0 +1,291 @@ +package org.gcube.data.access.storagehub.handlers; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import javax.inject.Singleton; +import javax.jcr.ItemExistsException; +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; + +import org.apache.jackrabbit.util.Text; +import org.apache.jackrabbit.value.BinaryValue; +import org.apache.jackrabbit.value.BooleanValue; +import org.apache.jackrabbit.value.DateValue; +import org.apache.jackrabbit.value.LongValue; +import org.apache.jackrabbit.value.StringValue; +import org.gcube.common.storagehub.model.Metadata; +import org.gcube.common.storagehub.model.NodeConstants; +import org.gcube.common.storagehub.model.annotations.Attribute; +import org.gcube.common.storagehub.model.annotations.AttributeRootNode; +import org.gcube.common.storagehub.model.annotations.ListNodes; +import org.gcube.common.storagehub.model.annotations.MapAttribute; +import org.gcube.common.storagehub.model.annotations.NodeAttribute; +import org.gcube.common.storagehub.model.annotations.RootNode; +import org.gcube.common.storagehub.model.items.AbstractFileItem; +import org.gcube.common.storagehub.model.items.Item; +import org.gcube.common.storagehub.model.types.ItemAction; +import org.gcube.common.storagehub.model.types.NodeProperty; +import org.gcube.data.access.storagehub.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class Item2NodeConverter { + + private static final Logger logger = LoggerFactory.getLogger(Item2NodeConverter.class); + + public Node getNode(Session session, Node parentNode, T item){ + try { + String primaryType= ClassHandler.instance().getNodeType(item.getClass()); + Node newNode = parentNode.addNode(Text.escapeIllegalJcrChars(item.getName()), primaryType); + //newNode.setPrimaryType(primaryType); + for (Field field : retrieveAllFields(item.getClass())){ + if (field.isAnnotationPresent(Attribute.class)){ + Attribute attribute = field.getAnnotation(Attribute.class); + if (attribute.isReadOnly()) continue; + field.setAccessible(true); + try{ + //Class returnType = field.getType(); + logger.debug("creating node - added field {}",field.getName()); + Values values = getObjectValue(field.getType(), field.get(item)); + if (values.isMulti()) newNode.setProperty(attribute.value(), values.getValues()); + else newNode.setProperty(attribute.value(), values.getValue()); + } catch (Exception e ) { + logger.warn("error setting value for attribute "+attribute.value(),e); + } + } else if (field.isAnnotationPresent(NodeAttribute.class)){ + NodeAttribute nodeAttribute = field.getAnnotation(NodeAttribute.class); + if (nodeAttribute.isReadOnly()) continue; + String nodeName = nodeAttribute.value(); + logger.debug("retrieving field node "+field.getName()); + field.setAccessible(true); + try{ + Object obj = field.get(item); + if (obj!=null) + iterateItemNodeAttributeFields(obj, newNode, nodeName); + } catch (Exception e ) { + logger.warn("error setting value",e); + } + + + } + } + return newNode; + } catch (RepositoryException e) { + logger.error("error writing repository",e); + throw new RuntimeException(e); + } + + } + + private void iterateItemNodeAttributeFields(Object object, Node parentNode, String nodeName) throws Exception{ + + AttributeRootNode attributeRootNode = object.getClass().getAnnotation(AttributeRootNode.class); + + Node newNode; + try { + if (attributeRootNode==null || attributeRootNode.value().isEmpty()) + newNode = parentNode.addNode(nodeName); + else newNode = parentNode.addNode(nodeName, attributeRootNode.value()); + }catch(ItemExistsException iee) { + newNode = parentNode.getNode(nodeName); + } + + for (Field field : retrieveAllFields(object.getClass())){ + if (field.isAnnotationPresent(Attribute.class)){ + Attribute attribute = field.getAnnotation(Attribute.class); + if (attribute.isReadOnly()) continue; + field.setAccessible(true); + try{ + @SuppressWarnings("rawtypes") + Class returnType = field.getType(); + Values values = getObjectValue(returnType, field.get(object)); + if (values.isMulti()) newNode.setProperty(attribute.value(), values.getValues()); + else newNode.setProperty(attribute.value(), values.getValue()); + } catch (Exception e ) { + logger.warn("error setting value",e); + } + } else if (field.isAnnotationPresent(MapAttribute.class)){ + //logger.debug("found field {} of type annotated as MapAttribute in class {}", field.getName(), clazz.getName()); + field.setAccessible(true); + Map mapToset = (Map)field.get(object); + for (Entry entry : mapToset.entrySet()) + try{ + Values values = getObjectValue(entry.getValue().getClass(), entry.getValue()); + if (values.isMulti()) newNode.setProperty(entry.getKey(), values.getValues()); + else newNode.setProperty(entry.getKey(), values.getValue()); + } catch (Exception e ) { + logger.warn("error setting value",e); + } + + } else if (field.isAnnotationPresent(ListNodes.class)){ + logger.debug("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName()); + field.setAccessible(true); + List toSetList = (List) field.get(object); + + int i = 0; + for (Object obj: toSetList){ + + logger.debug("the current node {} has a list",newNode.getName()); + + iterateItemNodeAttributeFields(obj,newNode, field.getName()+(i++)); + } + } + } + } + + @SuppressWarnings({ "rawtypes" }) + private Values getObjectValue(Class returnType, Object value) throws Exception{ + + if (returnType.equals(String.class)) return new Values(new StringValue((String) value)); + if (returnType.isEnum()) return new Values(new StringValue(((Enum) value).toString())); + if (returnType.equals(Calendar.class)) return new Values(new DateValue((Calendar) value)); + if (returnType.equals(Boolean.class) || returnType.equals(boolean.class)) return new Values(new BooleanValue((Boolean) value)); + if (returnType.equals(Long.class) || returnType.equals(long.class)) return new Values(new LongValue((Long) value)); + if (returnType.equals(Integer.class) || returnType.equals(int.class)) return new Values(new LongValue((Long) value)); + if (returnType.isArray()) { + if (returnType.getComponentType().equals(Byte.class) + || returnType.getComponentType().equals(byte.class)) + return new Values(new BinaryValue((byte[]) value)); + else { + Object[] arrayObj= (Object[])value; + Value[] arrayValue = new Value[arrayObj.length]; + int i=0; + for (Object val: arrayObj) + arrayValue[i++]=getObjectValue(returnType.getComponentType(), val).getValue(); + return new Values(arrayValue); + } + } + throw new Exception(String.format("class %s not recognized",returnType.getName())); + } + + private Set retrieveAllFields(Class clazz){ + + Set fields = new HashSet(); + Class currentClass = clazz; + do{ + List fieldsFound = Arrays.asList(currentClass.getDeclaredFields()); + fields.addAll(fieldsFound); + }while ((currentClass =currentClass.getSuperclass())!=null); + return fields; + } + + public void replaceContent(Session session, Node node, F item, ItemAction action){ + try { + + node.setPrimaryType(item.getClass().getAnnotation(RootNode.class).value()); + Node contentNode = node.getNode(NodeConstants.CONTENT_NAME); + contentNode.setPrimaryType(item.getContent().getClass().getAnnotation(AttributeRootNode.class).value()); + + node.setProperty(NodeProperty.LAST_MODIFIED.toString(), item.getLastModificationTime()); + node.setProperty(NodeProperty.LAST_MODIFIED_BY.toString(), item.getLastModifiedBy()); + node.setProperty(NodeProperty.LAST_ACTION.toString(), action.name()); + + for (Field field : retrieveAllFields(item.getContent().getClass())){ + if (field.isAnnotationPresent(Attribute.class)){ + Attribute attribute = field.getAnnotation(Attribute.class); + if (attribute.isReadOnly()) continue; + field.setAccessible(true); + try{ + //Class returnType = field.getType(); + Values values = getObjectValue(field.getType(), field.get(item.getContent())); + if (values.isMulti()) contentNode.setProperty(attribute.value(), values.getValues() ); + else contentNode.setProperty(attribute.value(), values.getValue()); + + } catch (Exception e ) { + logger.warn("error setting value for attribute "+attribute.value(),e); + } + } + } + + } catch (RepositoryException e) { + logger.error("error writing repository",e); + throw new RuntimeException(e); + } + + } + + public void updateMetadataNode(Session session, Node node, Map meta, String login){ + try { + + //TODO: make a method to update item not only metadata, check if the new metadata has an intersection with the old one to remove properties not needed + + Utils.setPropertyOnChangeNode(node, login, ItemAction.UPDATED); + + Node metadataNode; + try { + metadataNode = node.getNode(NodeProperty.METADATA.toString()); + }catch (PathNotFoundException e) { + metadataNode = node.addNode(NodeProperty.METADATA.toString()); + } + + for (Field field : retrieveAllFields(Metadata.class)){ + if (field.isAnnotationPresent(MapAttribute.class)){ + //logger.debug("found field {} of type annotated as MapAttribute in class {}", field.getName(), clazz.getName()); + field.setAccessible(true); + for (Entry entry : meta.entrySet()) + try{ + Values values = getObjectValue(entry.getValue().getClass(), entry.getValue()); + if (values.isMulti()) metadataNode.setProperty(entry.getKey(), values.getValues()); + else metadataNode.setProperty(entry.getKey(), values.getValue()); + } catch (Exception e ) { + logger.warn("error setting value",e); + } + + } + } + + } catch (RepositoryException e) { + logger.error("error writing repository",e); + throw new RuntimeException(e); + } + + } + + public static class Values { + private Value singleValue; + private Value[] multivalues; + + boolean multi = false; + + public Values(Value singleValue) { + super(); + this.singleValue = singleValue; + this.multivalues = null; + multi = false; + } + + public Values(Value[] multivalues) { + super(); + multi = true; + this.multivalues = multivalues; + this.singleValue = null; + } + + public boolean isMulti() { + return multi; + } + + public Value getValue(){ + if (multi) throw new RuntimeException("must be accessed as multi values"); + return this.singleValue; + } + + public Value[] getValues(){ + if (!multi) throw new RuntimeException("must be accessed as single value"); + return this.multivalues; + } + + } + +} diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/ItemHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java similarity index 55% rename from src/main/java/org/gcube/data/access/storagehub/handlers/ItemHandler.java rename to src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java index 3f2ebf5..2c6916a 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/ItemHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java @@ -10,9 +10,9 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; +import javax.inject.Singleton; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; @@ -20,52 +20,59 @@ import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; -import javax.jcr.Session; import javax.jcr.Value; +import org.apache.commons.io.IOUtils; import org.apache.jackrabbit.util.Text; -import org.apache.jackrabbit.value.BinaryValue; -import org.apache.jackrabbit.value.BooleanValue; -import org.apache.jackrabbit.value.DateValue; -import org.apache.jackrabbit.value.LongValue; -import org.apache.jackrabbit.value.StringValue; -import org.gcube.common.storagehub.model.NodeConstants; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.annotations.Attribute; import org.gcube.common.storagehub.model.annotations.AttributeRootNode; import org.gcube.common.storagehub.model.annotations.ListNodes; import org.gcube.common.storagehub.model.annotations.MapAttribute; import org.gcube.common.storagehub.model.annotations.NodeAttribute; -import org.gcube.common.storagehub.model.annotations.RootNode; -import org.gcube.common.storagehub.model.items.AbstractFileItem; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; import org.gcube.common.storagehub.model.items.TrashItem; -import org.gcube.common.storagehub.model.items.nodes.Content; -import org.gcube.common.storagehub.model.types.ItemAction; -import org.gcube.common.storagehub.model.types.NodeProperty; import org.reflections.Configuration; import org.reflections.Reflections; import org.reflections.util.ConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ItemHandler { - - private static final Logger logger = LoggerFactory.getLogger(ItemHandler.class); - - private static ClassHandler classHandler = new ClassHandler(); +@Singleton +public class Node2ItemConverter { + private static final Logger logger = LoggerFactory.getLogger(Node2ItemConverter.class); + private static HashMap> typeToSubtypeMap = new HashMap<>(); - - public static T getItem(Node node, List excludes) throws Exception { - + + public T getFilteredItem(Node node, List excludes, Class nodeTypeToInclude) throws RepositoryException, BackendGenericError{ @SuppressWarnings("unchecked") - Class classToHandle = (Class)classHandler.get(node.getPrimaryNodeType().getName()); + Class classToHandle = (Class)ClassHandler.instance().get(node.getPrimaryNodeType().getName()); + if (nodeTypeToInclude!=null && !(nodeTypeToInclude.isAssignableFrom(classToHandle))) return null; + else return retrieveItem(node, excludes, classToHandle); + } - T item = classToHandle.newInstance(); + public T getItem(Node node, List excludes) throws RepositoryException, BackendGenericError{ + @SuppressWarnings("unchecked") + Class classToHandle = (Class)ClassHandler.instance().get(node.getPrimaryNodeType().getName()); + return retrieveItem(node, excludes, classToHandle); + } + + + private T retrieveItem(Node node, List excludes, Class classToHandle) throws RepositoryException, BackendGenericError{ + T item; + try { + item = classToHandle.newInstance(); + }catch (Exception e) { + throw new BackendGenericError(e); + } item.setId(node.getIdentifier()); item.setName(Text.unescapeIllegalJcrChars(node.getName())); + item.setPath(Text.unescapeIllegalJcrChars(node.getPath())); + item.setLocked(node.isLocked()); item.setPrimaryType(node.getPrimaryNodeType().getName()); Item parent = null ; @@ -74,7 +81,7 @@ public class ItemHandler { item.setShared(true); }else { try { - parent = ItemHandler.getItem(node.getParent(), Arrays.asList("hl:accounting","jcr:content")); + parent = getItem(node.getParent(), Excludes.ALL); item.setShared(parent.isShared()); } catch(Exception e) { item.setShared(false); @@ -86,7 +93,7 @@ public class ItemHandler { else { try { if (parent==null) - parent = ItemHandler.getItem(node.getParent(), Arrays.asList("hl:accounting","jcr:content")); + parent = getItem(node.getParent(), Excludes.ALL); item.setTrashed(parent.isTrashed()); } catch(Exception e) { item.setTrashed(false); @@ -107,7 +114,7 @@ public class ItemHandler { try{ Class returnType = field.getType(); field.set(item, getPropertyValue(returnType, node.getProperty(attribute.value()))); - + logger.debug("retrieve item - added field {}",field.getName()); }catch(PathNotFoundException e){ logger.trace("the current node dosn't contain {} property",attribute.value()); } catch (Exception e ) { @@ -132,10 +139,11 @@ public class ItemHandler { } } + return item; } - private static T iterateNodeAttributeFields(Class clazz, Node node) throws Exception{ + private T iterateNodeAttributeFields(Class clazz, Node node) throws Exception{ T obj = clazz.newInstance(); for (Field field : retrieveAllFields(clazz)){ if (field.isAnnotationPresent(Attribute.class)){ @@ -148,7 +156,7 @@ public class ItemHandler { }catch(PathNotFoundException e){ logger.trace("the current node dosn't contain {} property",attribute.value()); } catch (Exception e ) { - logger.warn("error setting value",e); + logger.warn("error setting value {}",e.getMessage()); } } else if (field.isAnnotationPresent(MapAttribute.class)){ logger.trace("found field {} of type annotated as MapAttribute in class {} and node name {}", field.getName(), clazz.getName(), node.getName()); @@ -162,12 +170,12 @@ public class ItemHandler { if (!exclude.isEmpty() && prop.getName().startsWith(exclude)) continue; try{ logger.trace("adding {} in the map",prop.getName()); - + mapToset.put(prop.getName(), getPropertyValue(prop)); }catch(PathNotFoundException e){ - logger.warn("the property {} is not mapped",prop.getName(),e); + logger.warn("the property {} is not mapped",prop.getName()); } catch (Exception e ) { - logger.warn("error setting value",e); + logger.warn("error setting value {}",e.getMessage()); } } } @@ -232,7 +240,7 @@ public class ItemHandler { } @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Object getPropertyValue(Class returnType, Property prop) throws Exception{ + private Object getPropertyValue(Class returnType, Property prop) throws Exception{ if (returnType.equals(String.class)) return prop.getString(); if (returnType.isEnum()) return Enum.valueOf(returnType, prop.getString()); if (returnType.equals(Calendar.class)) return prop.getDate(); @@ -241,11 +249,7 @@ public class ItemHandler { if (returnType.equals(Integer.class) || returnType.equals(int.class)) return prop.getLong(); if (returnType.isArray()) { if (prop.getType()==PropertyType.BINARY) { - byte[] bytes = new byte[32000]; - - try (InputStream stream = prop.getBinary().getStream()){ - stream.read(bytes); - } + byte[] bytes = IOUtils.toByteArray(prop.getBinary().getStream()); return bytes; } else { Object[] ret= getArrayValue(prop); @@ -255,59 +259,6 @@ public class ItemHandler { throw new Exception(String.format("class %s not recognized",returnType.getName())); } - - - @SuppressWarnings({ "rawtypes" }) - private static Value getObjectValue(Class returnType, Object value) throws Exception{ - if (returnType.equals(String.class)) return new StringValue((String) value); - if (returnType.isEnum()) return new StringValue(((Enum) value).toString()); - if (returnType.equals(Calendar.class)) return new DateValue((Calendar) value); - if (returnType.equals(Boolean.class) || returnType.equals(boolean.class)) return new BooleanValue((Boolean) value); - if (returnType.equals(Long.class) || returnType.equals(long.class)) return new LongValue((Long) value); - if (returnType.equals(Integer.class) || returnType.equals(int.class)) return new LongValue((Long) value); - if (returnType.isArray()) { - if (returnType.getComponentType().equals(Byte.class) - || returnType.getComponentType().equals(byte.class)) - return new BinaryValue((byte[]) value); - } - throw new Exception(String.format("class %s not recognized",returnType.getName())); - } - - - private static Object[] getArrayValue(Property prop) throws Exception{ - Object[] values = new Object[prop.getValues().length]; - int i = 0; - for (Value value : prop.getValues()) - values[i++] = getSingleValue(value); - return values; - } - - - private static Object getPropertyValue(Property prop) throws Exception{ - if (prop.isMultiple()){ - Object[] values = new Object[prop.getValues().length]; - int i = 0; - for (Value value : prop.getValues()) - values[i++] = getSingleValue(value); - return values; - } else - return getSingleValue(prop.getValue()); - - } - - private static Object getSingleValue(Value value) throws Exception{ - switch (value.getType()) { - case PropertyType.DATE: - return value.getDate(); - case PropertyType.BOOLEAN: - return value.getBoolean(); - case PropertyType.LONG: - return value.getDate(); - default: - return value.getString(); - } - } - private static Set retrieveAllFields(Class clazz){ Set fields = new HashSet(); @@ -318,124 +269,46 @@ public class ItemHandler { }while ((currentClass =currentClass.getSuperclass())!=null); return fields; } + + private Object getPropertyValue(Property prop) throws Exception{ + if (prop.isMultiple()){ + Object[] values = new Object[prop.getValues().length]; + int i = 0; + for (Value value : prop.getValues()) + values[i++] = getSingleValue(value); + return values; + } else + return getSingleValue(prop.getValue()); - public static Node createNodeFromItem(Session session, Node parentNode, T item){ + } + + private Object getSingleValue(Value value) throws Exception{ + switch (value.getType()) { + case PropertyType.DATE: + return value.getDate(); + case PropertyType.BOOLEAN: + return value.getBoolean(); + case PropertyType.LONG: + return value.getDate(); + default: + return value.getString(); + } + } + + private Object[] getArrayValue(Property prop) throws Exception{ + Object[] values = new Object[prop.getValues().length]; + int i = 0; + for (Value value : prop.getValues()) + values[i++] = getSingleValue(value); + return values; + } + + public boolean checkNodeType(Node node, Class classToCompare) throws BackendGenericError{ try { - - //TODO: must understand this place is for name or title - String primaryType= classHandler.getNodeType(item.getClass()); - Node newNode = parentNode.addNode(item.getTitle(), primaryType); - //newNode.setPrimaryType(primaryType); - for (Field field : retrieveAllFields(item.getClass())){ - if (field.isAnnotationPresent(Attribute.class)){ - Attribute attribute = field.getAnnotation(Attribute.class); - if (attribute.isReadOnly()) continue; - field.setAccessible(true); - try{ - //Class returnType = field.getType(); - newNode.setProperty(attribute.value(), getObjectValue(field.getType(), field.get(item))); - - } catch (Exception e ) { - logger.warn("error setting value for attribute "+attribute.value(),e); - } - } else if (field.isAnnotationPresent(NodeAttribute.class)){ - NodeAttribute nodeAttribute = field.getAnnotation(NodeAttribute.class); - if (nodeAttribute.isReadOnly()) continue; - String nodeName = nodeAttribute.value(); - logger.debug("retrieving field node "+field.getName()); - field.setAccessible(true); - try{ - iterateItemNodeAttributeFields(field.get(item), newNode, nodeName); - } catch (Exception e ) { - logger.warn("error setting value",e); - } - - - } - } - return newNode; - } catch (RepositoryException e) { - logger.error("error writing repository",e); - throw new RuntimeException(e); - } - - } - - private static void iterateItemNodeAttributeFields(Object object, Node parentNode, String nodeName) throws Exception{ - - AttributeRootNode attributeRootNode = object.getClass().getAnnotation(AttributeRootNode.class); - Node newNode = parentNode.addNode(nodeName, attributeRootNode.value()); - //newNode.setPrimaryType(attributeRootNode.value()); - for (Field field : retrieveAllFields(object.getClass())){ - if (field.isAnnotationPresent(Attribute.class)){ - Attribute attribute = field.getAnnotation(Attribute.class); - if (attribute.isReadOnly()) continue; - field.setAccessible(true); - try{ - @SuppressWarnings("rawtypes") - Class returnType = field.getType(); - newNode.setProperty(attribute.value(), getObjectValue(returnType, field.get(object))); - } catch (Exception e ) { - logger.warn("error setting value",e); - } - } else if (field.isAnnotationPresent(MapAttribute.class)){ - //logger.debug("found field {} of type annotated as MapAttribute in class {}", field.getName(), clazz.getName()); - field.setAccessible(true); - Map mapToset = (Map)field.get(object); - for (Entry entry : mapToset.entrySet()) - try{ - newNode.setProperty(entry.getKey(), getObjectValue(entry.getValue().getClass(), entry.getValue())); - } catch (Exception e ) { - logger.warn("error setting value",e); - } - - } else if (field.isAnnotationPresent(ListNodes.class)){ - logger.debug("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName()); - field.setAccessible(true); - List toSetList = (List) field.get(object); - - int i = 0; - for (Object obj: toSetList){ - - logger.debug("the current node {} has a list",newNode.getName()); - - iterateItemNodeAttributeFields(obj,newNode, field.getName()+(i++)); - } - } + return (node.isNodeType(ClassHandler.instance().getNodeType(classToCompare))); + }catch (Throwable e) { + throw new BackendGenericError(e); } } - - public static void replaceContent(Session session, Node node, F item){ - try { - - node.setPrimaryType(item.getClass().getAnnotation(RootNode.class).value()); - Node contentNode = node.getNode(NodeConstants.CONTENT_NAME); - contentNode.setPrimaryType(item.getContent().getClass().getAnnotation(AttributeRootNode.class).value()); - - node.setProperty(NodeProperty.LAST_MODIFIED.toString(), item.getLastModificationTime()); - node.setProperty(NodeProperty.LAST_MODIFIED_BY.toString(), item.getLastModifiedBy()); - node.setProperty(NodeProperty.LAST_ACTION.toString(), ItemAction.UPDATED.name()); - - for (Field field : retrieveAllFields(item.getContent().getClass())){ - if (field.isAnnotationPresent(Attribute.class)){ - Attribute attribute = field.getAnnotation(Attribute.class); - if (attribute.isReadOnly()) continue; - field.setAccessible(true); - try{ - //Class returnType = field.getType(); - contentNode.setProperty(attribute.value(), getObjectValue(field.getType(), field.get(item.getContent()))); - - } catch (Exception e ) { - logger.warn("error setting value for attribute "+attribute.value(),e); - } - } - } - - } catch (RepositoryException e) { - logger.error("error writing repository",e); - throw new RuntimeException(e); - } - - } - + } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java new file mode 100644 index 0000000..5d064ea --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java @@ -0,0 +1,186 @@ +package org.gcube.data.access.storagehub.handlers; + +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +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.UserNotAuthorizedException; +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.types.ItemAction; +import org.gcube.contentmanagement.blobstorage.service.IClient; +import org.gcube.data.access.storagehub.AuthorizationChecker; +import org.gcube.data.access.storagehub.Constants; +import org.gcube.data.access.storagehub.Utils; +import org.gcube.data.access.storagehub.accounting.AccountingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class TrashHandler { + + private static Logger log = LoggerFactory.getLogger(TrashHandler.class); + + @Inject + VersionHandler versionHandler; + + @Inject + AccountingHandler accountingHandler; + + @Inject + AuthorizationChecker authChecker; + + @Inject + Item2NodeConverter item2Node; + + public void removeNodes(Session ses, List itemsToDelete) throws RepositoryException{ + log.debug("defnitively removing nodes with ids {}",itemsToDelete); + final String login = AuthorizationProvider.instance.get().getClient().getId(); + final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); + //String parentPath = itemToDelete.getParentPath(); + try { + ses.getWorkspace().getLockManager().lock(trashFolder.getPath(), true, true, 0,login); + Set contentIdsToDelete = new HashSet<>(); + + for (Item trashItem: itemsToDelete) { + try { + Node trashItemNode = ses.getNodeByIdentifier(trashItem.getId()); + List trashChildren = Utils.getItemList(trashItemNode, Excludes.GET_ONLY_CONTENT, null, true, null); + for (Item itemContentToRetrieve: trashChildren) + Utils.getAllContentIds(ses, contentIdsToDelete, itemContentToRetrieve, versionHandler); + trashItemNode.remove(); + }catch (Exception e) { + log.warn("error removing item with id {}",trashItem.getId(), e); + } + } + log.debug("content ids to remove are {}",contentIdsToDelete); + + + //TODO: make it as an authorizableTask + String user = AuthorizationProvider.instance.get().getClient().getId(); + new Thread() { + + public void run() { + for (String id: contentIdsToDelete) { + try { + IClient client = Utils.getStorageClient(user).getClient(); + client.remove().RFileById(id); + log.debug("file with id {} correctly removed on storage",id); + }catch(Throwable t) { + log.warn("error removing file on storage with id {}",id, t); + } + } + + } + }.start();; + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(trashFolder.getPath()); + } + } + + + public void moveToTrash(Session ses, Node nodeToDelete, Item item) throws RepositoryException, BackendGenericError{ + log.debug("moving node {} to trash ",item.getId()); + final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); + + final String login = AuthorizationProvider.instance.get().getClient().getId(); + + 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(AuthorizationProvider.instance.get().getClient().getId()); + 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(item.getOwner()); + 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(ses, 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, ses.getNodeByIdentifier(item.getParentId()), true); + }catch(Throwable 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) throws RepositoryException, BackendGenericError, UserNotAuthorizedException{ + log.debug("restoring node from trash"); + final String login = AuthorizationProvider.instance.get().getClient().getId(); + //final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); + Node originalParent = ses.getNodeByIdentifier(item.getOriginalParentId()); + authChecker.checkWriteAuthorizationControl(ses, originalParent.getIdentifier(), false ); + + ses.getWorkspace().getLockManager().lock(originalParent.getPath(), true, true, 0,login); + 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); + String newNodePath = Paths.append(Paths.getPath(originalParent.getPath()), itemToMove.getName()).toPath(); + ses.move(itemToMove.getPath(), newNodePath); + Utils.setPropertyOnChangeNode(ses.getNode(newNodePath), login, ItemAction.MOVED); + ses.removeItem(item.getPath()); + ses.save(); + return ses.getNode(newNodePath).getIdentifier(); + + } + +} diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/VRE.java b/src/main/java/org/gcube/data/access/storagehub/handlers/VRE.java index 7cd268f..90740c5 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/VRE.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/VRE.java @@ -7,6 +7,7 @@ import java.util.concurrent.Future; import javax.jcr.Repository; import javax.jcr.SimpleCredentials; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.items.Item; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,12 +15,12 @@ import org.slf4j.LoggerFactory; public class VRE { private static final Logger logger = LoggerFactory.getLogger(VRE.class); - + private Item vreFolder; private Future> result; private VREQueryRetriever vreQueryRetriever; private ExecutorService executor; - + public VRE(Item item, Repository repository, SimpleCredentials credentials, ExecutorService executor) { super(); this.vreFolder = item; @@ -27,20 +28,24 @@ public class VRE { vreQueryRetriever = new VREQueryRetriever(repository, credentials, vreFolder); result = executor.submit(vreQueryRetriever); } - + public Item getVreFolder() { return vreFolder; } - - public synchronized List getRecents() throws Exception{ - logger.trace("getting recents"); - if (result.isDone()) { - result = executor.submit(vreQueryRetriever); + + public synchronized List getRecents() throws BackendGenericError{ + try { + logger.trace("getting recents"); + if (result.isDone()) { + result = executor.submit(vreQueryRetriever); + } + return result.get(); + }catch(Exception ee) { + throw new BackendGenericError(ee); } - return result.get(); } - - + + } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java b/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java index fed397c..955f486 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java @@ -17,6 +17,7 @@ import javax.jcr.observation.Event; import javax.jcr.observation.EventJournal; import javax.jcr.query.Query; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.NodeConstants; import org.gcube.common.storagehub.model.items.Item; import org.gcube.data.access.storagehub.Constants; @@ -28,13 +29,15 @@ public class VREQueryRetriever implements Callable> { private static final Logger logger = LoggerFactory.getLogger(VREQueryRetriever.class); private static final int CACHE_DIMENSION = 50; - + private Repository repository; private Credentials credentials; private Item vreFolder; List cachedList = new ArrayList<>(CACHE_DIMENSION); long lastTimestamp =0; + private Node2ItemConverter node2Item = new Node2ItemConverter(); + public VREQueryRetriever(Repository repository, Credentials credentials, Item vreFolder) { super(); @@ -59,7 +62,7 @@ public class VREQueryRetriever implements Callable> { logger.trace("query for recents took {}",System.currentTimeMillis()-start); while (it.hasNext()) { Node node = it.nextNode(); - Item item =ItemHandler.getItem(node, Arrays.asList(NodeConstants.ACCOUNTING_NAME)); + Item item =node2Item.getItem(node, Excludes.EXCLUDE_ACCOUNTING); cachedList.add(item); logger.trace("adding item {} with node {}",item.getTitle(), node.getName()); } @@ -101,7 +104,7 @@ public class VREQueryRetriever implements Callable> { Node nodeAdded = ses.getNode(event.getPath()); if (nodeAdded.isNodeType("nthl:workspaceLeafItem")) { logger.trace("node added event received with name {}", nodeAdded.getName()); - Item item = ItemHandler.getItem(nodeAdded, Arrays.asList(NodeConstants.ACCOUNTING_NAME)); + Item item = node2Item.getItem(nodeAdded, Arrays.asList(NodeConstants.ACCOUNTING_NAME)); insertItemInTheRightPlace(item); } } @@ -114,7 +117,7 @@ public class VREQueryRetriever implements Callable> { logger.trace("event property changed on {} with value {} and parent {}",property.getName(), property.getValue().getString(), property.getParent().getPath()); String identifier = property.getParent().getIdentifier(); cachedList.removeIf(i -> i.getId().equals(identifier)); - Item item = ItemHandler.getItem(property.getParent(), Arrays.asList(NodeConstants.ACCOUNTING_NAME)); + Item item = node2Item.getItem(property.getParent(), Excludes.EXCLUDE_ACCOUNTING); insertItemInTheRightPlace(item); } } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/VersionHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/VersionHandler.java index d37382d..a97628a 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/VersionHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/VersionHandler.java @@ -13,7 +13,6 @@ import javax.jcr.version.VersionIterator; import javax.jcr.version.VersionManager; import org.apache.jackrabbit.JcrConstants; -import org.apache.jackrabbit.core.version.VersionManagerImplBase; import org.gcube.common.storagehub.model.NodeConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/content/ContentHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/content/ContentHandler.java index 1dbbc27..913512b 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/content/ContentHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/content/ContentHandler.java @@ -7,7 +7,7 @@ import org.gcube.common.storagehub.model.items.nodes.Content; public interface ContentHandler { - void initiliseSpecificContent(InputStream is) throws Exception; + void initiliseSpecificContent(InputStream is, String fileName) throws Exception; Content getContent(); diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/content/GenericFileHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/content/GenericFileHandler.java index 56b466e..d5423e1 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/content/GenericFileHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/content/GenericFileHandler.java @@ -12,7 +12,7 @@ public class GenericFileHandler implements ContentHandler{ Content content = new Content(); @Override - public void initiliseSpecificContent(InputStream is) throws Exception {} + public void initiliseSpecificContent(InputStream is, String filename) throws Exception {} @Override public Content getContent() { diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/content/ImageHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/content/ImageHandler.java index 657301e..8250899 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/content/ImageHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/content/ImageHandler.java @@ -1,57 +1,55 @@ package org.gcube.data.access.storagehub.handlers.content; +import java.awt.Color; +import java.awt.Graphics2D; import java.awt.Image; +import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; -import java.util.Base64; import java.util.Calendar; import javax.imageio.ImageIO; +import org.apache.commons.io.FilenameUtils; import org.gcube.common.storagehub.model.annotations.MimeTypeHandler; import org.gcube.common.storagehub.model.items.ImageFile; import org.gcube.common.storagehub.model.items.nodes.ImageContent; import org.gcube.common.storagehub.model.types.ItemAction; - -import ij.ImagePlus; -import ij.io.FileSaver; -import ij.process.ImageProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @MimeTypeHandler({"image/gif", "image/jpeg","image/png","image/svg+xml"}) public class ImageHandler implements ContentHandler{ - private static final int THUMB_MAX_DIM = 50; - + private static final int THUMB_MAX_DIM = 300; + private ImageContent content = new ImageContent(); - + + private static final Logger logger = LoggerFactory.getLogger(ImageHandler.class); @Override - public void initiliseSpecificContent(InputStream is) throws Exception { - BufferedImage buf = ImageIO.read(is); - content.setWidth(Long.valueOf(buf.getWidth())); - content.setHeight(Long.valueOf(buf.getHeight())); - - ImagePlus image = new ImagePlus("thumbnail", buf); - - int thumbSize[] = getThumbnailDimension(buf.getWidth(), buf.getHeight()); - - try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream thumbstream = getThumbnailAsPng(image, thumbSize[0], thumbSize[1])){ + public void initiliseSpecificContent(InputStream is, String fileName) throws Exception { + Image image = javax.imageio.ImageIO.read(is); - byte[] imgbuf = new byte[1024]; - int read = -1; - while ((read=thumbstream.read(imgbuf))!=-1) - baos.write(imgbuf, 0, read); - - content.setThumbnailHeight(Long.valueOf(thumbSize[1])); - content.setThumbnailWidth(Long.valueOf(thumbSize[0])); - content.setThumbnailData(Base64.getEncoder().encode(baos.toByteArray())); - } catch (Exception e) { - // TODO: handle exception - } + int width = image.getWidth(null); + int height = image.getHeight(null); + + content.setWidth(Long.valueOf(width)); + content.setHeight(Long.valueOf(height)); + + try { + int[] dimension = getThumbnailDimension(width, height); + + byte[] buf = transform(image, fileName, dimension[0], dimension[1]).toByteArray(); + + + content.setThumbnailHeight(Long.valueOf(dimension[1])); + content.setThumbnailWidth(Long.valueOf(dimension[0])); + content.setThumbnailData(buf); + }catch(Throwable t) { + logger.warn("thumbnail for file {} cannot be created ", fileName,t); + } } @@ -93,29 +91,6 @@ public class ImageHandler implements ContentHandler{ return dimension; } - - private InputStream getThumbnailAsPng(ImagePlus img, int thumbWidth, - int thumbHeight) throws IOException { - - InputStream stream = null; - ImageProcessor processor = img.getProcessor(); - try{ - Image thumb = processor.resize(thumbWidth, thumbHeight).createImage(); - thumb = thumb.getScaledInstance(thumbWidth,thumbHeight,Image.SCALE_SMOOTH); - - FileSaver fs = new FileSaver(new ImagePlus("",thumb)); - File tmpThumbFile = File.createTempFile("THUMB", "TMP"); - tmpThumbFile.deleteOnExit(); - - fs.saveAsPng(tmpThumbFile.getAbsolutePath()); - stream = new FileInputStream(tmpThumbFile); - - }catch (Exception e) { - throw new RuntimeException(e); - } - return stream; - } - @Override public ImageFile buildItem(String name, String description, String login) { ImageFile item = new ImageFile(); @@ -133,4 +108,28 @@ public class ImageHandler implements ContentHandler{ return item; } + + public ByteArrayOutputStream transform(Image image, + String originalFile, int thumbWidth, + int thumbHeight) + throws Exception { + + BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB); + + Graphics2D graphics2D = thumbImage.createGraphics(); + graphics2D.setBackground(Color.WHITE); + graphics2D.setPaint(Color.WHITE); + graphics2D.fillRect(0, 0, thumbWidth, thumbHeight); + graphics2D.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null); + + String ext = FilenameUtils.getExtension(originalFile); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(thumbImage, ext, os); + return os; + } + } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/content/PdfHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/content/PdfHandler.java index c6c9c69..a63fd5e 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/content/PdfHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/content/PdfHandler.java @@ -24,7 +24,7 @@ public class PdfHandler implements ContentHandler { @Override - public void initiliseSpecificContent(InputStream is) throws Exception { + public void initiliseSpecificContent(InputStream is, String fileName) throws Exception { PdfReader reader = new PdfReader(is); content.setNumberOfPages(Long.valueOf(reader.getNumberOfPages())); content.setVersion(String.valueOf(reader.getPdfVersion())); diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java index 69af40a..948a79e 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java @@ -1,10 +1,12 @@ package org.gcube.data.access.storagehub.services; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.security.AccessControlEntry; import javax.jcr.security.Privilege; @@ -17,10 +19,14 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import org.apache.commons.compress.archivers.ArchiveException; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.storagehub.model.acls.ACL; import org.gcube.common.storagehub.model.acls.AccessType; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; import org.gcube.common.storagehub.model.types.ACLList; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.handlers.CredentialHandler; @@ -72,15 +78,18 @@ public class ACLManager { acl.setAccessTypes(types); acls.add(acl); } - return new ACLList(acls); - }catch (Exception e) { - log.error("error gettign ACL",e); - throw new WebApplicationException(e); + + }catch(RepositoryException re){ + log.error("jcr error extracting archive", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re)); + }catch(StorageHubException she ){ + log.error("error creating file item", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } - + return new ACLList(acls); } diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java index 6fac34b..3d55be8 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java @@ -1,12 +1,16 @@ package org.gcube.data.access.storagehub.services; -import java.util.Arrays; +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.jcr.Node; +import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.security.AccessControlEntry; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import javax.servlet.ServletContext; @@ -15,25 +19,34 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.NodeConstants; -import org.gcube.common.storagehub.model.Paths; import org.gcube.common.storagehub.model.acls.AccessType; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; +import org.gcube.common.storagehub.model.exceptions.InvalidItemException; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; +import org.gcube.common.storagehub.model.items.SharedFolder; +import org.gcube.common.storagehub.model.types.ItemAction; +import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.common.storagehub.model.types.PrimaryNodeType; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.CredentialHandler; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.gcube.smartgears.utils.InnerMethodName; import org.glassfish.jersey.media.multipart.FormDataParam; @@ -63,111 +76,350 @@ public class ItemSharing { @Inject VersionHandler versionHandler; - + + @Inject Node2ItemConverter node2Item; + @Inject Item2NodeConverter item2Node; + @PUT @Path("{id}/share") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.MULTIPART_FORM_DATA) public String share(@FormDataParam("users") Set users, @FormDataParam("defaultAccessType") AccessType accessType){ - InnerMethodName.instance.set("findChildrenByNamePattern"); + InnerMethodName.instance.set("shareFolder"); Session ses = null; + String toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkWriteAuthorizationControl(ses, id, false); - Item item = ItemHandler.getItem(ses.getNodeByIdentifier(id), Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.CONTENT_NAME, NodeConstants.METADATA_NAME)); - - if (! (item instanceof FolderItem) && ((FolderItem) item).isShared() && Utils.hasSharedChildren((FolderItem)item, ses) && item.getOwner().equals(login)) - throw new Exception("item with id "+id+" cannot be shared"); + //Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL); if (accessType==null) accessType = AccessType.READ_ONLY; - if (users.isEmpty()) - throw new Exception("users is empty"); - - - String sharedFolderName = item.getId(); - - String newNodePath = Constants.SHARED_FOLDER_PATH+"/"+sharedFolderName; - - /*ses.getWorkspace().getLockManager().lock(newNodePath, true, true, 0,login); - - try { - */ - ses.move(item.getPath(),newNodePath); - - Node sharedFolderNode = ses.getNode(newNodePath); - - sharedFolderNode.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER); - - Node usersNode =null; - if (sharedFolderNode.hasNode("hl:users")) - usersNode = sharedFolderNode.getNode("hl:users"); - else - usersNode = sharedFolderNode.addNode("hl:users"); + if (users==null || users.isEmpty()) + throw new InvalidCallParameters("users is empty"); + Node nodeToShare = ses.getNodeByIdentifier(id); + + boolean alreadyShared = false; + + Node sharedFolderNode; + if (!node2Item.checkNodeType(nodeToShare, SharedFolder.class)) + sharedFolderNode= shareFolder(nodeToShare, ses); + else { + sharedFolderNode = nodeToShare; + alreadyShared = true; + } ses.save(); - ses.getWorkspace().getLockManager().lock(newNodePath, true, true, 0,login); + ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,login); try { AccessControlManager acm = ses.getAccessControlManager(); JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); - //setting data for ADMINISTRATOR - org.gcube.common.storagehub.model.Path adminFolderPath = Paths.append(Utils.getHomePath(), item.getName()); - - log.debug("trying to clone dir from {} to {}", sharedFolderNode.getPath(), adminFolderPath.toPath()); - - ses.getWorkspace().clone(ses.getWorkspace().getName(), sharedFolderNode.getPath(), adminFolderPath.toPath(), false); - String adminRootWSId = ses.getNode(Utils.getHomePath().toPath()).getIdentifier(); - - Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; - acls.addAccessControlEntry(AccessControlUtils.getPrincipal(ses, login), adminPrivileges ); - - usersNode.setProperty(login, String.format("%s/%s",adminRootWSId,item.getName())); - - users.remove(login); - Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) }; - for (String user : users) { - try { - org.gcube.common.storagehub.model.Path userFolderPath = Paths.append(Utils.getHomePath(user), item.getName()); - ses.getWorkspace().clone(ses.getWorkspace().getName(), sharedFolderNode.getPath(), userFolderPath.toPath(), false); - String userRootWSId = ses.getNode(Utils.getHomePath(user).toPath()).getIdentifier(); - acls.addAccessControlEntry(AccessControlUtils.getPrincipal(ses, user), userPrivileges ); - usersNode.setProperty(user, String.format("%s/%s",userRootWSId,item.getName())); - }catch(Throwable t) { - log.warn("error sharing folder with user {}",user); - } + if (!alreadyShared) { + Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; + addUserToSharing(sharedFolderNode, ses, login, adminPrivileges, acls); + users.remove(login); } + Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) }; + for (String user : users) + try { + addUserToSharing(sharedFolderNode, ses, user, userPrivileges, acls); + }catch(Exception e){ + log.warn("error adding user {} to sharing of folder {}", user, sharedFolderNode.getName()); + } + acm.setPolicy(sharedFolderNode.getPath(), acls); - accountingHandler.shareFolder(item.getTitle(), users, ses, sharedFolderNode, false); + accountingHandler.createShareFolder(sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), users, ses, sharedFolderNode, false); ses.save(); - return sharedFolderNode.getIdentifier(); + toReturn = sharedFolderNode.getIdentifier(); } finally { - ses.getWorkspace().getLockManager().unlock(newNodePath); + ses.getWorkspace().getLockManager().unlock(sharedFolderNode.getPath()); } - - - }catch(Throwable e){ - log.error("error sharing node with id {}",id,e); - throw new WebApplicationException(e); + }catch(RepositoryException re){ + log.error("jcr sharing", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re)); + }catch(StorageHubException she ){ + log.error("error sharing", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } + + return toReturn; + } + + + private Node shareFolder(Node node, Session ses) throws RepositoryException, BackendGenericError, StorageHubException{ + String login = AuthorizationProvider.instance.get().getClient().getId(); + + if (!node2Item.checkNodeType(node, FolderItem.class) || Utils.hasSharedChildren(node) || !node.getProperty(NodeProperty.OWNER.toString()).getString().equals(login)) + throw new InvalidItemException("item with id "+id+" cannot be shared"); + + String sharedFolderName = node.getIdentifier(); + + String newNodePath = Constants.SHARED_FOLDER_PATH+"/"+sharedFolderName; + + ses.move(node.getPath(),newNodePath); + + Node sharedFolderNode = ses.getNode(newNodePath); + + sharedFolderNode.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER); + + return sharedFolderNode; + } + + private void addUserToSharing(Node sharedFolderNode, Session ses, String user, Privilege[] userPrivileges, JackrabbitAccessControlList acls) throws RepositoryException{ + ses.getWorkspace().clone(ses.getWorkspace().getName(), sharedFolderNode.getPath(), sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), false); + String userRootWSId = ses.getNode(Utils.getHomePath(user).toPath()).getIdentifier(); + acls.addAccessControlEntry(AccessControlUtils.getPrincipal(ses, user), userPrivileges ); + Node usersNode =null; + if (sharedFolderNode.hasNode(NodeConstants.USERS_NAME)) + usersNode = sharedFolderNode.getNode(NodeConstants.USERS_NAME); + else + usersNode = sharedFolderNode.addNode(NodeConstants.USERS_NAME); + usersNode.setProperty(user, String.format("%s/%s",userRootWSId,sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString())); + } + + + @PUT + @Path("{id}/unshare") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + public String unshare(@FormDataParam("users") Set users){ + InnerMethodName.instance.set("unshareFolder"); + String login = AuthorizationProvider.instance.get().getClient().getId(); + Session ses = null; + String toReturn = null; + try { + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + Node sharedNode = ses.getNodeByIdentifier(id); + Item item = node2Item.getItem(sharedNode, Excludes.ALL); + if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder()) + throw new InvalidItemException("item with id "+id+" cannot be unshared"); + SharedFolder sharedItem =(SharedFolder) item; + + Set usersInSharedFolder = new HashSet<>(sharedItem.getUsers().getValues().keySet()); + usersInSharedFolder.removeAll(users); + + if (users==null || users.size()==0 || usersInSharedFolder.size()<=1) + return unshareAll(login, ses, sharedItem); + + ses.getWorkspace().getLockManager().lock(sharedNode.getPath(), true, true, 0,login); + try { + if (users.size()==1 && users.contains(login)) + toReturn = unshareCaller(login, ses, sharedItem); + else toReturn = unsharePartial(users, login, ses, sharedItem); + }finally { + ses.getWorkspace().getLockManager().unlock(sharedNode.getPath()); + } + }catch(RepositoryException re){ + log.error("jcr unsharing", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re)); + }catch(StorageHubException she ){ + log.error("error unsharing", she); + GXOutboundErrorResponse.throwException(she); + }finally{ + + if (ses!=null) + ses.logout(); + } + return toReturn; + } + + + + + private String unshareAll(String login, Session ses, SharedFolder item) throws StorageHubException, BackendGenericError, RepositoryException{ + + authChecker.checkAdministratorControl(ses, item); + if (!login.equals(item.getOwner())) + throw new UserNotAuthorizedException("user "+login+" not authorized to unshare all"); + + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + + ses.getWorkspace().getLockManager().lock(sharedItemNode.getPath(), true, true, 0,login); + Node unsharedNode; + try { + log.debug("user list is empty, I'm going to remove also the shared dir"); + //TODO: take the admin folder and remove his clone then move the shared folder from share to the user home and change the folder type + String adminDirPath = (String)item.getUsers().getValues().get(login); + String[] splitString = adminDirPath.split("/"); + String parentDirectoryId = splitString[0]; + String directoryName = splitString[1]; + Node parentNode = ses.getNodeByIdentifier(parentDirectoryId); + log.debug("parent node path is {}/{}",parentNode.getPath(), directoryName); + + Node adminNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); + adminNode.removeShare(); + + unsharedNode = createUnsharedFolder(ses, parentNode, directoryName, item.getDescription(), login); + + List itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null); + + for (Item itemCopy: itemsToCopy) { + Node itemToCopyNode = ses.getNodeByIdentifier(itemCopy.getId()); + log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath()); + ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName())); + } + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath()); + } + sharedItemNode.removeSharedSet(); + ses.save(); + log.debug("all the users have been removed, the folder is totally unshared"); + + return unsharedNode.getIdentifier(); } + + + + private String unshareCaller(String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException{ + + if (login.equals(item.getOwner())) + throw new InvalidCallParameters("the callor is the owner, the folder cannot be unshared"); + + if (item.getUsers().getValues().get(login)==null) + throw new InvalidCallParameters("the folder is not shared with user "+login); + + Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); + + String parentId = removeSharingForUser(login, ses, item); + + AccessControlManager acm = ses.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); + + AccessControlEntry entryToDelete= null; + for (AccessControlEntry ace :acls.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(login)) { + entryToDelete = ace; + break; + } + + } + if (entryToDelete!=null) + acls.removeAccessControlEntry(entryToDelete); + + log.debug("removed Access control entry for user {}",login); + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); + usersNode.remove(); + Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); + + item.getUsers().getValues().entrySet().stream().filter(entry -> !entry.getKey().equals(login)).forEach(entry-> {try { + newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); + } catch (Exception e) { + log.error("error adding property to shared node users node under "+item.getId()); + }}); + + acm.setPolicy(sharedFolderNode.getPath(), acls); + + ses.save(); + + return parentId; + + } + + + + + private String unsharePartial(Set usersToUnshare, String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException { + authChecker.checkAdministratorControl(ses, (SharedFolder)item); + if (usersToUnshare.contains(item.getOwner())) + throw new UserNotAuthorizedException("user "+login+" not authorized to unshare owner"); + + Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); + + AccessControlManager acm = ses.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); + + for (String user : usersToUnshare) { + removeSharingForUser(user, ses, item); + + AccessControlEntry entryToDelete= null; + for (AccessControlEntry ace :acls.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(login)) { + entryToDelete = ace; + break; + } + + } + if (entryToDelete!=null) + acls.removeAccessControlEntry(entryToDelete); + } + + log.debug("removed Access control entry for user {}",login); + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); + usersNode.remove(); + Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); + + item.getUsers().getValues().entrySet().stream().filter(entry -> !usersToUnshare.contains(entry.getKey())).forEach(entry-> {try { + newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); + } catch (Exception e) { + log.error("error adding property to shared node users node under "+item.getId()); + }}); + + acm.setPolicy(sharedFolderNode.getPath(), acls); + + ses.save(); + + return item.getId(); + + } + + + public String removeSharingForUser(String user, Session ses, SharedFolder item) throws RepositoryException { + String userDirPath = (String)item.getUsers().getValues().get(user); + if (userDirPath==null) return null; + String[] splitString = userDirPath.split("/"); + String parentDirectoryId = splitString[0]; + String directoryName = splitString[1]; + Node parentNode = ses.getNodeByIdentifier(parentDirectoryId); + Node userNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); + userNode.removeShare(); + accountingHandler.createUnshareFolder(directoryName, ses, parentNode, false); + log.debug("directory removed for user {}",user); + return parentDirectoryId; + } + + + private Node createUnsharedFolder(Session ses, Node destinationNode, String name, String description, String login) { + FolderItem item = new FolderItem(); + Calendar now = Calendar.getInstance(); + item.setName(name); + item.setTitle(name); + item.setDescription(description); + //item.setCreationTime(now); + item.setHidden(false); + item.setLastAction(ItemAction.CREATED); + item.setLastModificationTime(now); + item.setLastModifiedBy(login); + item.setOwner(login); + + //to inherit hidden property + //item.setHidden(destinationItem.isHidden()); + + Node newNode = item2Node.getNode(ses, destinationNode, item); + return newNode; + } + + } diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java index be4f204..3fe1f4d 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java @@ -1,49 +1,67 @@ package org.gcube.data.access.storagehub.services; -import static org.gcube.common.storagehub.model.NodeConstants.ACCOUNTING_NAME; -import static org.gcube.common.storagehub.model.NodeConstants.CONTENT_NAME; - import java.io.BufferedInputStream; +import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; -import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.inject.Inject; +import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.tika.config.TikaConfig; import org.apache.tika.detect.Detector; import org.apache.tika.io.TikaInputStream; import org.apache.tika.metadata.Metadata; +import org.gcube.common.authorization.library.AuthorizedTasks; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +import org.gcube.common.storagehub.model.Excludes; +import org.gcube.common.storagehub.model.NodeConstants; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.IdNotFoundException; +import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; +import org.gcube.common.storagehub.model.exceptions.InvalidItemException; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.items.AbstractFileItem; import org.gcube.common.storagehub.model.items.FolderItem; +import org.gcube.common.storagehub.model.items.GCubeItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.types.ItemAction; +import org.gcube.contentmanagement.blobstorage.service.IClient; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.MetaInfo; import org.gcube.data.access.storagehub.MultipleOutputStream; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.CredentialHandler; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.gcube.data.access.storagehub.handlers.content.ContentHandler; import org.gcube.data.access.storagehub.handlers.content.ContentHandlerFactory; @@ -78,237 +96,318 @@ public class ItemsCreator { @Inject AccountingHandler accountingHandler; + @Inject Node2ItemConverter node2Item; + @Inject Item2NodeConverter item2Node; + - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) //@Path("/{id}/create/{type:(?!FILE)[^/?$]*}") + @POST + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Path("/{id}/create/FOLDER") - public Response createItem(@PathParam("id") String id, @PathParam("type") String type,@QueryParam("name") String name, @QueryParam("description") String description){ - InnerMethodName.instance.set(String.format("createItem(%s)",type)); - log.info("create generic item called"); + public String createFolder(@PathParam("id") String id, @FormParam("name") String name, @FormParam("description") String description) { + InnerMethodName.instance.set("createItem(FOLDER)"); + log.info("create folder item called"); Session ses = null; - Item destinationItem = null; + String toReturn = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); - //TODO check if it is possible to change all the ACL on a workspace ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - //validate input parameters for Item Type - - //if(!type.equals("FOLDER")) throw new IllegalAccessException("invalid item type"); - log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); - Node destination = ses.getNodeByIdentifier(id); - destinationItem = ItemHandler.getItem(destination,Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME)); - - if (!(destinationItem instanceof FolderItem)) throw new Exception("an Item must be created into a directory"); - - authChecker.checkWriteAuthorizationControl(ses, destinationItem.getId(), true); - - ses.getWorkspace().getLockManager().lock(destinationItem.getPath(), true, true, 0,login); - - FolderItem item = new FolderItem(); - Calendar now = Calendar.getInstance(); - item.setName(name); - item.setTitle(name); - item.setDescription(description); - //item.setCreationTime(now); - item.setHidden(false); - item.setLastAction(ItemAction.CREATED); - item.setLastModificationTime(now); - item.setLastModifiedBy(login); - item.setOwner(login); - - //to inherit hidden property - //item.setHidden(destinationItem.isHidden()); - - log.debug("item prepared, fulfilling content"); - - log.debug("content prepared"); - Node newNode = ItemHandler.createNodeFromItem(ses, destination, item); - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false); - ses.save(); - log.info("item with id {} correctly created",newNode.getIdentifier()); - return Response.ok(newNode.getIdentifier()).build(); - }catch(Exception e){ - log.error("error creating item", e); - throw new WebApplicationException(e); - } finally{ - if (ses!=null){ - if (destinationItem!=null) - try { - if (ses.getWorkspace().getLockManager().isLocked(destinationItem.getPath())) - ses.getWorkspace().getLockManager().unlock(destinationItem.getPath()); - } catch (Throwable t){ - log.warn("error unlocking {}", destinationItem.getPath(), t); - } - ses.logout(); + + Node destination; + try { + destination = ses.getNodeByIdentifier(id); + }catch(ItemNotFoundException inf) { + throw new IdNotFoundException(id); } + + if (!node2Item.checkNodeType(destination, FolderItem.class)) + throw new InvalidItemException("the destination item is not a folder"); + + authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier(), true); + + + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); + Node newNode; + try { + newNode = Utils.createFolderInternally(ses, destination, name, description, login, accountingHandler); + ses.save(); + } finally { + ses.getWorkspace().getLockManager().unlock(destination.getPath()); + } + + log.info("item with id {} correctly created",newNode.getIdentifier()); + toReturn = newNode.getIdentifier(); + }catch(StorageHubException she ){ + log.error("error creating item", she); + GXOutboundErrorResponse.throwException(she); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally{ + if (ses!=null) + ses.logout(); + } + return toReturn; } + @POST + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{id}/create/GCUBEITEM") + public String createGcubeItem(@PathParam("id") String id, GCubeItem item) { + InnerMethodName.instance.set("createItem(GCUBEITEM)"); + log.info("create Gcube item called"); + Session ses = null; + String toReturn = null; + + try{ + final String login = AuthorizationProvider.instance.get().getClient().getId(); + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + Node destination; + try { + destination = ses.getNodeByIdentifier(id); + }catch(ItemNotFoundException inf) { + throw new IdNotFoundException(id); + } + + if (!node2Item.checkNodeType(destination, FolderItem.class)) + throw new InvalidItemException("the destination item is not a folder"); + authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier(), true); + + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); + + Node newNode; + try { + newNode = Utils.createGcubeItemInternally(ses, destination, item.getName(), item.getDescription(), login, item, accountingHandler); + ses.save(); + } finally { + ses.getWorkspace().getLockManager().unlock(destination.getPath()); + } + + log.info("item with id {} correctly created",newNode.getIdentifier()); + toReturn = newNode.getIdentifier(); + }catch(StorageHubException she ){ + log.error("error creating item", she); + GXOutboundErrorResponse.throwException(she); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally{ + if (ses!=null) + ses.logout(); + } + return toReturn; + } + @POST @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces(MediaType.APPLICATION_JSON) + @Produces(MediaType.TEXT_PLAIN) @Path("/{id}/create/FILE") - public Response createFileItem(@PathParam("id") String id, @FormDataParam("name") String name, + public String createFileItem(@PathParam("id") String id, @FormDataParam("name") String name, @FormDataParam("description") String description, @FormDataParam("file") InputStream stream, @FormDataParam("file") FormDataContentDisposition fileDetail){ InnerMethodName.instance.set("createItem(FILE)"); - + Session ses = null; - Item destinationItem = null; + String toReturn = null; try{ - if (name==null || name.trim().isEmpty() || description ==null) throw new Exception("name or description are null"); + if (name==null || name.trim().isEmpty() || description ==null) throw new InvalidCallParameters("name or description are null"); final String login = AuthorizationProvider.instance.get().getClient().getId(); - //TODO check if it is possible to change all the ACL on a workspace ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - //TODO: validate input parameters for Item Type Node destination = ses.getNodeByIdentifier(id); log.info("create file called with filename {} in dir {} ", name, destination.getPath() ); - destinationItem = ItemHandler.getItem(destination,Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME)); - if (!(destinationItem instanceof FolderItem)) throw new Exception("an Item must be copyed to another directory"); - - ses.getWorkspace().getLockManager().lock(destinationItem.getPath(), true, true, 0,login); - - ContentHandler handler = getContentHandler(stream , name, destinationItem.getPath()); - - AbstractFileItem item =handler.buildItem(name, description, login); - - //to inherit hidden property - //item.setHidden(destinationItem.isHidden()); - - log.debug("item prepared, fulfilling content"); - log.debug("content prepared"); + if (!node2Item.checkNodeType(destination, FolderItem.class)) + throw new InvalidItemException("the destination item is not a folder"); + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); + Node newNode; try { - newNode = ses.getNode(org.gcube.common.storagehub.model.Paths.append(org.gcube.common.storagehub.model.Paths.getPath(destinationItem.getPath()), name).toPath()); - authChecker.checkWriteAuthorizationControl(ses, newNode.getIdentifier(), false); - versionHandler.checkoutContentNode(newNode, ses); - log.trace("replacing content of class {}",item.getContent().getClass()); - ItemHandler.replaceContent(ses, newNode,item); - }catch(PathNotFoundException pnf) { - log.info("creating new node"); - authChecker.checkWriteAuthorizationControl(ses, destinationItem.getId(), true); - newNode = ItemHandler.createNodeFromItem(ses, destination, item); - versionHandler.makeVersionableContent(newNode, ses); + newNode = createFileItemInternally(ses, destination, stream, name, description, login); + ses.save(); + } finally { + ses.getWorkspace().getLockManager().unlock(destination.getPath()); } - - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, newNode, false); - - ses.save(); + versionHandler.checkinContentNode(newNode, ses); log.info("file with id {} correctly created",newNode.getIdentifier()); - return Response.ok(newNode.getIdentifier()).build(); - }catch(Throwable e){ - log.error("error creating item", e); - return Response.serverError().build(); + toReturn = newNode.getIdentifier(); + }catch(RepositoryException re ){ + log.error("jcr error creating file item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating file item", re)); + }catch(StorageHubException she ){ + log.error("error creating file item", she); + GXOutboundErrorResponse.throwException(she); } finally{ - if (ses!=null){ - if (destinationItem!=null) - try { - if (ses.getWorkspace().getLockManager().isLocked(destinationItem.getPath())) - ses.getWorkspace().getLockManager().unlock(destinationItem.getPath()); - } catch (Throwable t){ - log.warn("error unlocking {}", destinationItem.getPath(), t); - } + if (ses!=null) ses.logout(); - } + } + return toReturn; + + } + + + + private Node createFileItemInternally(Session ses, Node destinationNode, InputStream stream, String name, String description, String login) throws RepositoryException, UserNotAuthorizedException, BackendGenericError{ + + ContentHandler handler = getContentHandler(stream , name, destinationNode.getPath(), login); + + AbstractFileItem item =handler.buildItem(name, description, login); + + //to inherit hidden property + //item.setHidden(destinationItem.isHidden()); + + log.debug("item prepared, fulfilling content"); + log.debug("content prepared"); + + Node newNode; + try { + newNode = ses.getNode(org.gcube.common.storagehub.model.Paths.append(org.gcube.common.storagehub.model.Paths.getPath(destinationNode.getPath()), name).toPath()); + authChecker.checkWriteAuthorizationControl(ses, newNode.getIdentifier(), false); + versionHandler.checkoutContentNode(newNode, ses); + log.trace("replacing content of class {}",item.getContent().getClass()); + item2Node.replaceContent(ses, newNode,item, ItemAction.UPDATED); + }catch(PathNotFoundException pnf) { + log.info("creating new node"); + authChecker.checkWriteAuthorizationControl(ses, destinationNode.getIdentifier(), true); + newNode = item2Node.getNode(ses, destinationNode, item); + versionHandler.makeVersionableContent(newNode, ses); } + accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, newNode, false); + return newNode; } - /* + @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/create/ARCHIVE") - public Response uploadArchive(@PathParam("id") String id, @FormDataParam("folderName") String folderName, + public String uploadArchive(@PathParam("id") String id, @FormDataParam("parentFolderName") String parentFolderName, @FormDataParam("file") InputStream stream, @FormDataParam("file") FormDataContentDisposition fileDetail){ - InnerMethodName.instance.set("createItem(FILE)"); - + InnerMethodName.instance.set("createItem(ARCHIVE)"); + Session ses = null; - Item destinationItem = null; + String toReturn = null; try{ - if (folderName==null) throw new Exception("new folder name is null"); + if (parentFolderName==null) throw new InvalidCallParameters("new folder name is null"); + final String login = AuthorizationProvider.instance.get().getClient().getId(); - //TODO check if it is possible to change all the ACL on a workspace ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - //TODO: validate input parameters for Item Type Node destination = ses.getNodeByIdentifier(id); + + if (!node2Item.checkNodeType(destination, FolderItem.class)) + throw new InvalidItemException("the destination item is not a folder"); + + authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier() , true); - destinationItem = ItemHandler.getItem(destination,Arrays.asList(ACCOUNTING_NAME,CONTENT_NAME)); - if (!(destinationItem instanceof FolderItem)) throw new Exception("destination item is not a folder"); + ses.getWorkspace().getLockManager().lock(destination.getPath(), true, true, 0,login); + Node parentDirectoryNode = null; - ses.getWorkspace().getLockManager().lock(destinationItem.getPath(), true, true, 0,login); - - ContentHandler handler = getContentHandler(stream , name, destinationItem.getPath()); - - AbstractFileItem item =handler.buildItem(name, description, login); - - //to inherit hidden property - //item.setHidden(destinationItem.isHidden()); - - log.debug("item prepared, fulfilling content"); - log.debug("content prepared"); - - Node newNode; try { - newNode = ses.getNode(org.gcube.common.storagehub.model.Paths.append(org.gcube.common.storagehub.model.Paths.getPath(destinationItem.getPath()), name).toPath()); - authChecker.checkWriteAuthorizationControl(ses, newNode.getIdentifier(), false); - versionHandler.checkoutContentNode(newNode, ses); - log.trace("replacing content of class {}",item.getContent().getClass()); - ItemHandler.replaceContent(ses, newNode,item); - }catch(PathNotFoundException pnf) { - log.info("creating new node"); - authChecker.checkWriteAuthorizationControl(ses, destinationItem.getId(), true); - newNode = ItemHandler.createNodeFromItem(ses, destination, item); - versionHandler.makeVersionableContent(newNode, ses); - } + parentDirectoryNode = Utils.createFolderInternally(ses, destination, parentFolderName, "", login, accountingHandler); - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, newNode, false); + Set fileNodes = new HashSet<>(); - ses.save(); - versionHandler.checkinContentNode(newNode, ses); - log.info("file with id {} correctly created",newNode.getIdentifier()); - return Response.ok(newNode.getIdentifier()).build(); - }catch(Throwable e){ - log.error("error creating item", e); - return Response.serverError().build(); - } finally{ - if (ses!=null){ - if (destinationItem!=null) - try { - if (ses.getWorkspace().getLockManager().isLocked(destinationItem.getPath())) - ses.getWorkspace().getLockManager().unlock(destinationItem.getPath()); - } catch (Throwable t){ - log.warn("error unlocking {}", destinationItem.getPath(), t); + + HashMap directoryNodeMap = new HashMap<>(); + + try (ArchiveInputStream input = new ArchiveStreamFactory() + .createArchiveInputStream(new BufferedInputStream(stream, 1024*64))){ + ArchiveEntry entry; + while ((entry = input.getNextEntry()) != null) { + if (entry.isDirectory()) { + String entirePath = entry.getName(); + String name = entirePath.replaceAll("(.*/)*(.*)/", "$2"); + String parentPath = entirePath.replaceAll("(.*/)*(.*)/", "$1"); + log.debug("creating directory with entire path {}, name {}, parentPath {} ", entirePath, name, parentPath); + Node createdNode; + if (parentPath.isEmpty()) { + createdNode = Utils.createFolderInternally(ses, parentDirectoryNode, name, "", login, accountingHandler); + }else { + Node parentNode = directoryNodeMap.get(parentPath); + createdNode = Utils.createFolderInternally(ses, parentNode, name, "", login, accountingHandler); + } + directoryNodeMap.put(entirePath, createdNode); + continue; + } else { + try { + String entirePath = entry.getName(); + String name = entirePath.replaceAll("(.*/)*(.*)", "$2"); + String parentPath = entirePath.replaceAll("(.*/)*(.*)", "$1"); + log.debug("creating file with entire path {}, name {}, parentPath {} ", entirePath, name, parentPath); + Node fileNode = null; + if (parentPath.isEmpty()) + fileNode = createFileItemInternally(ses, parentDirectoryNode, input, name, "", login); + else { + Node parentNode = directoryNodeMap.get(parentPath); + fileNode = createFileItemInternally(ses, parentNode, input, name, "", login); + } + fileNodes.add(fileNode); + }catch(Exception e) { + log.warn("error getting file {}",entry.getName(),e); + } + } } - ses.logout(); + + } + + ses.save(); + for (Node node : fileNodes) + versionHandler.checkinContentNode(node, ses); + toReturn = parentDirectoryNode.getIdentifier(); + + } finally { + try { + if (ses.getWorkspace().getLockManager().isLocked(destination.getPath())) + ses.getWorkspace().getLockManager().unlock(destination.getPath()); + } catch (Throwable t){ + log.warn("error unlocking {}", destination.getPath(), t); + } } + + }catch(RepositoryException | ArchiveException | IOException re){ + log.error("jcr error extracting archive", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re)); + }catch(StorageHubException she ){ + log.error("error creating file item", she); + GXOutboundErrorResponse.throwException(she); + } finally{ + if (ses!=null) + ses.logout(); + } - + return toReturn; } - */ - private ContentHandler getContentHandler(InputStream stream , String name, String path) throws Exception { - final MultipleOutputStream mos = new MultipleOutputStream(stream, 2); + private ContentHandler getContentHandler(InputStream stream , String name, String path, String login) throws BackendGenericError { + + + final MultipleOutputStream mos; + try{ + mos = new MultipleOutputStream(stream, 2); + }catch (IOException e) { + throw new BackendGenericError(e); + } Callable mimeTypeDector = new Callable() { @@ -317,7 +416,7 @@ public class ItemsCreator { ContentHandler handler =null; long start = System.currentTimeMillis(); log.debug("TIMING: reading the mimetype - start"); - try(BufferedInputStream is1 = new BufferedInputStream(mos.get(), 2048)){ + try(InputStream is1 = new BufferedInputStream(mos.get(), 1024*64)){ org.apache.tika.mime.MediaType mediaType = null; TikaConfig config = TikaConfig.getDefaultConfig(); Detector detector = config.getDetector(); @@ -330,7 +429,7 @@ public class ItemsCreator { handler = contenthandlerFactory.create(mimeType); is1.reset(); - handler.initiliseSpecificContent(is1); + handler.initiliseSpecificContent(is1, name); handler.getContent().setMimeType(mimeType); log.trace("TIMING: reading the mimetype - finished in {}",System.currentTimeMillis()-start); } catch (Throwable e) { @@ -346,17 +445,22 @@ public class ItemsCreator { @Override public MetaInfo call() throws Exception { - try { + try(InputStream is1 = mos.get()){ + String uid = UUID.randomUUID().toString(); + String remotePath= String.format("%s/%s-%s",path,uid,name); long start = System.currentTimeMillis(); log.debug("TIMING: sending the content to Storage - start"); - String remotePath= path+"/"+name; - String storageId = Utils.getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().put(true).LFile(mos.get()).RFile(remotePath); - long size = Utils.getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().getSize().RFileById(storageId); + IClient storageClient = Utils.getStorageClient(login).getClient(); + log.debug("TIMING: getting the client took {} ",System.currentTimeMillis()-start); + String storageId =storageClient.put(true).LFile(is1).RFile(remotePath); + log.debug("returned storage Id is {} for remotepath {}",storageId, remotePath); + log.debug("TIMING: sending the file took {} ",System.currentTimeMillis()-start); + long size = storageClient.getSize().RFileById(storageId); + log.debug("TIMING: sending the content to Storage - finished in {}",System.currentTimeMillis()-start); MetaInfo info = new MetaInfo(); info.setSize(size); info.setStorageId(storageId); info.setRemotePath(remotePath); - log.debug("TIMING: sending the content to Storage - finished in {}",System.currentTimeMillis()-start); return info; }catch (Throwable e) { log.error("error writing content"); @@ -366,21 +470,25 @@ public class ItemsCreator { } }; - Future detectorF = executor.submit(mimeTypeDector); - Future uploaderF = executor.submit(uploader); - + Future detectorF = executor.submit(AuthorizedTasks.bind(mimeTypeDector)); + Future uploaderF = executor.submit(AuthorizedTasks.bind(uploader)); + long start = System.currentTimeMillis(); - log.debug("TIMING: writing the strem - start"); - mos.startWriting(); - log.debug("TIMING: writing the stream - finished in {}",System.currentTimeMillis()-start); - - ContentHandler handler = detectorF.get(); - MetaInfo info = uploaderF.get(); - handler.getContent().setData("jcr:content"); - handler.getContent().setStorageId(info.getStorageId()); - handler.getContent().setSize(info.getSize()); - handler.getContent().setRemotePath(info.getRemotePath()); - return handler; + log.debug("TIMING: writing the stream - start"); + try { + mos.startWriting(); + log.debug("TIMING: writing the stream - finished in {}",System.currentTimeMillis()-start); + + ContentHandler handler = detectorF.get(); + MetaInfo info = uploaderF.get(); + handler.getContent().setData(NodeConstants.CONTENT_NAME); + handler.getContent().setStorageId(info.getStorageId()); + handler.getContent().setSize(info.getSize()); + handler.getContent().setRemotePath(info.getRemotePath()); + return handler; + }catch (Exception e) { + throw new BackendGenericError(e); + } } diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java index 8077137..e50e557 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java @@ -2,17 +2,15 @@ package org.gcube.data.access.storagehub.services; import java.io.InputStream; import java.io.OutputStream; +import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; +import java.util.Collections; import java.util.Deque; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.UUID; import java.util.zip.Deflater; import java.util.zip.ZipOutputStream; @@ -20,43 +18,55 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.version.Version; import javax.servlet.ServletContext; +import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; +import org.apache.commons.io.FilenameUtils; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.NodeConstants; 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.InvalidItemException; +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.SharedFolder; -import org.gcube.common.storagehub.model.items.TrashItem; import org.gcube.common.storagehub.model.items.VreFolder; import org.gcube.common.storagehub.model.service.ItemList; import org.gcube.common.storagehub.model.service.ItemWrapper; +import org.gcube.common.storagehub.model.service.VersionList; import org.gcube.common.storagehub.model.types.ItemAction; -import org.gcube.contentmanagement.blobstorage.service.IClient; +import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Range; import org.gcube.data.access.storagehub.SingleFileStreamingOutput; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; +import org.gcube.data.access.storagehub.handlers.ClassHandler; import org.gcube.data.access.storagehub.handlers.CredentialHandler; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; +import org.gcube.data.access.storagehub.handlers.TrashHandler; import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.gcube.smartgears.utils.InnerMethodName; import org.slf4j.Logger; @@ -85,7 +95,13 @@ public class ItemsManager { @Inject VersionHandler versionHandler; - + + @Inject + TrashHandler trashHandler; + + @Inject Node2ItemConverter node2Item; + @Inject Item2NodeConverter item2Node; + @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) @@ -96,10 +112,13 @@ public class ItemsManager { try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - toReturn = ItemHandler.getItem(ses.getNodeByIdentifier(id), excludes); - }catch(Throwable e){ - log.error("error reading the node children of {}",id,e); - throw new WebApplicationException(e); + toReturn = node2Item.getItem(ses.getNodeByIdentifier(id), excludes); + }catch(RepositoryException re){ + log.error("jcr error getting item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error searching item", re)); + }catch(StorageHubException she ){ + log.error("error getting item", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -120,10 +139,13 @@ public class ItemsManager { authChecker.checkReadAuthorizationControl(ses, id); NodeIterator it = ses.getNodeByIdentifier(id).getNodes(name); while (it.hasNext()) - toReturn.add(ItemHandler.getItem(it.nextNode(), excludes)); - }catch(Throwable e){ - log.error("error reading the node children of {} with name pattern",id,name,e); - throw new WebApplicationException(e); + toReturn.add(node2Item.getItem(it.nextNode(), excludes)); + }catch(RepositoryException re){ + log.error("jcr error searching item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error searching item", re)); + }catch(StorageHubException she ){ + log.error("error searching item", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -131,22 +153,27 @@ public class ItemsManager { return new ItemList(toReturn); } - + @GET @Path("{id}/children/count") @Produces(MediaType.APPLICATION_JSON) - public Long countById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes){ + public Long countById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes, @QueryParam("onlyType") String nodeType){ InnerMethodName.instance.set("countById"); Session ses = null; Long toReturn = null; + try{ + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - toReturn = Utils.getItemCount(ses.getNodeByIdentifier(id), showHidden==null?false:showHidden); - }catch(Throwable e){ - log.error("error reading the node children of {}",id,e); - throw new WebApplicationException(e); + toReturn = Utils.getItemCount(ses.getNodeByIdentifier(id), showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null); + }catch(RuntimeException | RepositoryException re){ + log.error("jcr error counting item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error counting item", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -157,17 +184,20 @@ public class ItemsManager { @GET @Path("{id}/children") @Produces(MediaType.APPLICATION_JSON) - public ItemList listById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes){ + public ItemList listById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes, @QueryParam("onlyType") String nodeType){ InnerMethodName.instance.set("listById"); Session ses = null; List toReturn = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden); - }catch(Throwable e){ - log.error("error reading the node children of {}",id,e); - throw new WebApplicationException(e); + toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null); + }catch(RepositoryException re){ + log.error("jcr error getting children", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting children", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -179,17 +209,20 @@ public class ItemsManager { @GET @Path("{id}/children/paged") @Produces(MediaType.APPLICATION_JSON) - public ItemList listByIdPaged(@QueryParam("showHidden") Boolean showHidden, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @QueryParam("exclude") List excludes){ + public ItemList listByIdPaged(@QueryParam("showHidden") Boolean showHidden, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @QueryParam("exclude") List excludes, @QueryParam("onlyType") String nodeType){ InnerMethodName.instance.set("listByIdPaged"); Session ses = null; List toReturn = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, new Range(start, limit),showHidden==null?false:showHidden); - }catch(Throwable e){ - log.error("error reading the node children of {}",id,e); - throw new WebApplicationException(e); + toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, new Range(start, limit),showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null); + }catch(RepositoryException re){ + log.error("jcr error getting paged children", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting paged children", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -199,32 +232,68 @@ public class ItemsManager { } @GET + @Produces(MediaType.APPLICATION_JSON) @Path("{id}/publiclink") - public URL getPubliclink() { + public URL getPublicLink(@QueryParam("version") String version) { InnerMethodName.instance.set("getPubliclink"); - //TODO: check who can call this method Session ses = null; + URL toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - Item item = ItemHandler.getItem(ses.getNodeByIdentifier(id), Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME)); + Node selectedNode = ses.getNodeByIdentifier(id); + + Item item = node2Item.getItem(selectedNode, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME)); - if (!(item instanceof AbstractFileItem)) throw new Exception("the select item is not a File"); + if (!(item instanceof AbstractFileItem)) throw new InvalidCallParameters("the choosen item is not a File"); - AbstractFileItem fileItem = (AbstractFileItem) item; + + if (version!=null) { + boolean versionFound = false; + VersionList versions = getVersions(); + for (org.gcube.common.storagehub.model.service.Version v: versions.getItemlist() ) + if (v.getName().equals(version)) { + versionFound = true; + break; + } + if (!versionFound) throw new InvalidCallParameters("the selected file has no version "+version); + } + + ses.getWorkspace().getLockManager().lock(selectedNode.getPath(), false, true, 0,login); + try { + selectedNode.setProperty(NodeProperty.IS_PUBLIC.toString(), true); + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(selectedNode.getPath()); + } + + String url = createPublicLink(version, id); - String url = Utils.getStorageClient(login).getClient().getHttpsUrl().RFileById(fileItem.getContent().getStorageId()); - return new URL(url); - }catch(Throwable e){ - log.error("error reading the node children of {}",id,e); - throw new WebApplicationException(e); + toReturn = new URL(url); + + }catch(RepositoryException | MalformedURLException re ){ + log.error("jcr error getting public link", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting public link", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } + return toReturn; + } + + + private String createPublicLink(String version, String id) { + String basepath = context.getInitParameter("resolver-basepath"); + String filePublicUrl = String.format("%s/%s",basepath, id); + if (version!=null) + filePublicUrl = String.format("%s/%s", filePublicUrl, version); + return filePublicUrl; } @GET @@ -233,26 +302,128 @@ public class ItemsManager { public ItemWrapper getRootSharedFolder(@QueryParam("exclude") List excludes){ InnerMethodName.instance.set("getRootSharedFolder"); Session ses = null; + Item sharedParent= null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - Item currentItem = ItemHandler.getItem(ses.getNodeByIdentifier(id), excludes); + Node currentNode =ses.getNodeByIdentifier(id); + Item currentItem = node2Item.getItem(currentNode, excludes); + if (!currentItem.isShared()) - throw new RuntimeException("this item is not shared"); - log.trace("current node is {}",currentItem.getPath()); - while (!(currentItem instanceof SharedFolder )) - currentItem = ItemHandler.getItem(ses.getNodeByIdentifier(currentItem.getParentId()), Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME, NodeConstants.CONTENT_NAME)); + throw new InvalidItemException("this item is not shared"); + log.trace("current node is {}",currentNode.getPath()); - return new ItemWrapper(currentItem); - - }catch(Throwable e){ - log.error("error retrieving shared root folder of node with id {}",id,e); - throw new WebApplicationException(e); + while (!node2Item.checkNodeType(currentNode, SharedFolder.class)) + currentNode = currentNode.getParent(); + + sharedParent = node2Item.getItem(currentNode, excludes); + + }catch(RepositoryException re ){ + log.error("jcr error getting rootSharedFolder", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting rootSharedFolder", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } - + return new ItemWrapper(sharedParent); + } + + @GET + @Path("{id}/versions") + @Produces(MediaType.APPLICATION_JSON) + public VersionList getVersions(){ + InnerMethodName.instance.set("getVersions"); + Session ses = null; + List versions = new ArrayList<>(); + try{ + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + authChecker.checkReadAuthorizationControl(ses, id); + + Node node = ses.getNodeByIdentifier(id); + + Item currentItem = node2Item.getItem(node, Excludes.GET_ONLY_CONTENT); + if (!(currentItem instanceof AbstractFileItem)) + throw new InvalidItemException("this item is not versioned"); + + List jcrVersions = versionHandler.getContentVersionHistory(node, ses); + + for (Version version: jcrVersions) { + boolean currentVersion = ((AbstractFileItem)currentItem).getContent().getStorageId().equals(version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString()); + versions.add(new org.gcube.common.storagehub.model.service.Version(version.getIdentifier(), version.getName(), version.getCreated(), currentVersion)); + } + }catch(RepositoryException re ){ + log.error("jcr error retrieving versions", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error retrieving versions", she); + GXOutboundErrorResponse.throwException(she); + }finally{ + if (ses!=null) + ses.logout(); + } + return new VersionList(versions); + } + + @GET + @Path("{id}/versions/{version}/download") + @Produces(MediaType.APPLICATION_JSON) + public Response downloadVersion(@PathParam("version") String versionName){ + InnerMethodName.instance.set("downloadSpecificVersion"); + Session ses = null; + try{ + String login = AuthorizationProvider.instance.get().getClient().getId(); + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + authChecker.checkReadAuthorizationControl(ses, id); + Node node = ses.getNodeByIdentifier(id); + Item currentItem = node2Item.getItem(node, Excludes.ALL); + if (!(currentItem instanceof AbstractFileItem)) + throw new InvalidItemException("this item is not a file"); + + List jcrVersions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(id), ses); + + for (Version version: jcrVersions) { + log.debug("retrieved version id {}, name {}", version.getIdentifier(), version.getName()); + if (version.getName().equals(versionName)) { + long size = version.getFrozenNode().getProperty(NodeProperty.SIZE.toString()).getLong(); + String mimeType = version.getFrozenNode().getProperty(NodeProperty.MIME_TYPE.toString()).getString(); + String storageId = version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString(); + + final InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(storageId); + + String oldfilename = FilenameUtils.getBaseName(currentItem.getTitle()); + String ext = FilenameUtils.getExtension(currentItem.getTitle()); + + String fileName = String.format("%s_v%s.%s", oldfilename, version.getName(), ext); + + + accountingHandler.createReadObj(fileName, ses, node, true); + + StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); + + return Response + .ok(so) + .header("content-disposition","attachment; filename = \""+fileName+"\"") + .header("Content-Length", size) + .header("Content-Type", mimeType) + .build(); + } + } + + + }catch(RepositoryException re ){ + log.error("jcr error downloading version", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error downloading version", she); + GXOutboundErrorResponse.throwException(she); + }finally{ + if (ses!=null) + ses.logout(); + } + return Response.serverError().build(); } @GET @@ -267,26 +438,32 @@ public class ItemsManager { String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - Item currentItem = ItemHandler.getItem(ses.getNodeByIdentifier(id), excludes); - log.trace("current node is {}",currentItem.getPath()); - while (!(currentItem.getPath()+"/").equals(absolutePath.toPath())) { + Node currentNode = ses.getNodeByIdentifier(id); + Item currentItem = node2Item.getItem(currentNode, excludes); + log.trace("current node is {}",currentNode.getPath()); + while (!(currentNode.getPath()+"/").equals(absolutePath.toPath())) { if (currentItem instanceof SharedFolder){ Map users = ((SharedFolder) currentItem).getUsers().getValues(); String[] user = ((String)users.get(login)).split("/"); String parentId = user[0]; - currentItem = ItemHandler.getItem(ses.getNodeByIdentifier(parentId), excludes); + currentNode = ses.getNodeByIdentifier(parentId); + currentItem = node2Item.getItem(currentNode, excludes); - }else - currentItem = ItemHandler.getItem(ses.getNodeByIdentifier(currentItem.getParentId()), excludes); + }else { + currentNode = currentNode.getParent(); + currentItem = node2Item.getItem(currentNode, excludes); + } - - log.trace("current node is {}",currentItem.getPath()); + log.trace("current node is {}",currentNode.getPath()); toReturn.add(currentItem); } - }catch(Throwable e){ - log.error("error retrieving parents of node with id {}",id,e); - throw new WebApplicationException(e); + }catch(RepositoryException re ){ + log.error("jcr error getting anchestors", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting anchestors", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -297,20 +474,21 @@ public class ItemsManager { return new ItemList(toReturn); } - - - + + + @GET @Path("{id}/download") - public Response download(){ + public Response download(@QueryParam("exclude") List excludes){ InnerMethodName.instance.set("downloadById"); Session ses = null; + Response response = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); final Node node = ses.getNodeByIdentifier(id); authChecker.checkReadAuthorizationControl(ses, id); - final Item item = ItemHandler.getItem(node, null); + final Item item = node2Item.getItem(node, null); if (item instanceof AbstractFileItem){ AbstractFileItem fileItem =(AbstractFileItem) item; @@ -320,17 +498,18 @@ public class ItemsManager { StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); - return Response + response = Response .ok(so) - .header("content-disposition","attachment; filename = "+fileItem.getName()) + .header("content-disposition","attachment; filename = \""+fileItem.getName()+"\"") .header("Content-Length", fileItem.getContent().getSize()) + .header("Content-Type", fileItem.getContent().getMimeType()) .build(); } else if (item instanceof FolderItem){ try { - final Deque allNodes = Utils.getAllNodesForZip((FolderItem)item, ses, accountingHandler); - final org.gcube.common.storagehub.model.Path originalPath = Paths.getPath(item.getPath()); + final Deque allNodes = Utils.getAllNodesForZip((FolderItem)item, ses, accountingHandler, excludes); + final org.gcube.common.storagehub.model.Path originalPath = Paths.getPath(item.getParentPath()); StreamingOutput so = new StreamingOutput() { @Override @@ -349,30 +528,103 @@ public class ItemsManager { } }; - return Response + response = Response .ok(so) - .header("content-disposition","attachment; filename = directory.zip") + .header("content-disposition","attachment; filename = \""+item.getTitle()+".zip\"") + .header("Content-Type", "application/zip") .header("Content-Length", -1l) .build(); }finally { if (ses!=null) ses.save(); } - } else throw new Exception("item type not supported for download: "+item.getClass()); + } else throw new InvalidItemException("item type not supported for download: "+item.getClass()); - }catch(Exception e ){ - log.error("error downloading item content",e); - throw new WebApplicationException(e); + }catch(RepositoryException re ){ + log.error("jcr error download", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error download", she); + GXOutboundErrorResponse.throwException(she); } finally{ if (ses!=null) ses.logout(); } + + return response; } + @PUT @Path("{id}/move") - public Response move(@QueryParam("destinationId") String destinationId, @PathParam("id") String identifier){ + public String move(@FormParam("destinationId") String destinationId){ InnerMethodName.instance.set("move"); //TODO: check if identifier is The Workspace root, or the thras folder or the VREFolder root or if the item is thrashed Session ses = null; + try{ + final String login = AuthorizationProvider.instance.get().getClient().getId(); + + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + authChecker.checkWriteAuthorizationControl(ses, destinationId, true); + authChecker.checkWriteAuthorizationControl(ses, id, false); + + final Node nodeToMove = ses.getNodeByIdentifier(id); + final Node destination = ses.getNodeByIdentifier(destinationId); + Node originalParent = nodeToMove.getParent(); + + Item destinationItem = node2Item.getItem(destination,null); + final Item item = node2Item.getItem(nodeToMove, null); + + if (item instanceof SharedFolder) + throw new InvalidItemException("shared folder cannot be moved"); + + if (Constants.FOLDERS_TO_EXLUDE.contains(item.getTitle()) || Constants.FOLDERS_TO_EXLUDE.contains(destinationItem.getTitle())) + throw new InvalidItemException("protected folder cannot be moved"); + + if (!(destinationItem instanceof FolderItem)) + throw new InvalidItemException("destination item is not a folder"); + + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); + ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login); + try { + String uniqueName =(Utils.checkExistanceAndGetUniqueName(ses, destination, nodeToMove.getName())); + String newPath = String.format("%s/%s",destination.getPath(), uniqueName); + if (item instanceof FolderItem && Utils.hasSharedChildren(nodeToMove)) + throw new InvalidItemException("folder item with shared children cannot be moved"); + + ses.getWorkspace().move(nodeToMove.getPath(), newPath); + Utils.setPropertyOnChangeNode(ses.getNode(newPath), login, ItemAction.MOVED); + + String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null; + + accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting , ses, destination, false); + accountingHandler.createFolderRemoveObj(item.getTitle(), item.getClass().getSimpleName(), mimeTypeForAccounting, ses, originalParent, false); + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath()); + ses.getWorkspace().getLockManager().unlock(destination.getPath()); + } + + }catch(RepositoryException re ){ + log.error("jcr error moving item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error moving item", she); + GXOutboundErrorResponse.throwException(she); + } finally{ + if (ses!=null) { + ses.logout(); + } + } + return id; + } + + @PUT + @Path("{id}/copy") + public String copy(@FormParam("destinationId") String destinationId, @FormParam("fileName") String newFileName){ + InnerMethodName.instance.set("copy"); + //TODO: check if identifier is The Workspace root, or the trash folder or the VREFolder root or if the item is thrashed + Session ses = null; + String newFileIdentifier = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); //ses = RepositoryInitializer.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); @@ -380,81 +632,194 @@ public class ItemsManager { ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkWriteAuthorizationControl(ses, destinationId, true); - authChecker.checkWriteAuthorizationControl(ses, identifier, false); + authChecker.checkReadAuthorizationControl(ses, id); - final Node nodeToMove = ses.getNodeByIdentifier(identifier); + final Node nodeToCopy = ses.getNodeByIdentifier(id); final Node destination = ses.getNodeByIdentifier(destinationId); - Item destinationItem = ItemHandler.getItem(destination,null); + //Item destinationItem = node2Item.getItem(destination,null); - final Item item = ItemHandler.getItem(nodeToMove, null); + final Item item = node2Item.getItem(nodeToCopy, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME)); - if (item instanceof SharedFolder || item.isHidden() || destinationItem.isHidden()) - throw new Exception("shared folder cannot be moved or cannot not move hidden item"); - - if (Constants.FOLDERS_TO_EXLUDE.contains(item.getTitle()) || Constants.FOLDERS_TO_EXLUDE.contains(destinationItem.getTitle())) - throw new Exception("protected folder cannot be moved"); + if (item instanceof FolderItem) + throw new InvalidItemException("folder cannot be copied"); - ses.getWorkspace().getLockManager().lock(destinationItem.getPath(), true, true, 0,login); - ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login); + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); + ses.getWorkspace().getLockManager().lock(nodeToCopy.getPath(), true, true, 0,login); + try { + String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destination, newFileName); + String newPath= String.format("%s/%s", destination.getPath(), uniqueName); + ses.getWorkspace().copy(nodeToCopy.getPath(), newPath); + Node newNode = ses.getNode(newPath); + newFileIdentifier = newNode.getIdentifier(); + //TODO: copy on storage and modify content + if (item instanceof AbstractFileItem) { + String newStorageID = Utils.getStorageClient(login).getClient().copyFile().from(((AbstractFileItem)item).getContent().getStorageId()).to(newPath); + ((AbstractFileItem) item).getContent().setStorageId(newStorageID); + item2Node.replaceContent(ses, newNode, (AbstractFileItem) item, ItemAction.CLONED); + } else + Utils.setPropertyOnChangeNode(newNode, login, ItemAction.CLONED); + + String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null; + accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting, ses, destination, false); + + ses.save(); - if (item instanceof FolderItem){ + }finally { + ses.getWorkspace().getLockManager().unlock(nodeToCopy.getPath()); + ses.getWorkspace().getLockManager().unlock(destination.getPath()); + } - if (Utils.hasSharedChildren((FolderItem) item, ses)) throw new Exception("folder item with shared children cannot be moved"); - - ses.getWorkspace().move(nodeToMove.getPath(), destination.getPath()+"/"+nodeToMove.getName()); - }else - ses.getWorkspace().move(nodeToMove.getPath(), destination.getPath()+"/"+nodeToMove.getName()); - - //TODO: accounting - - ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath()); - ses.getWorkspace().getLockManager().unlock(destinationItem.getPath()); - ses.save(); - }catch(Exception e){ - log.error("error moving item with id {} in item with id {}",identifier, destinationId,e); - throw new WebApplicationException(e); + }catch(RepositoryException re ){ + log.error("jcr error moving item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error moving item", she); + GXOutboundErrorResponse.throwException(she); } finally{ if (ses!=null) { ses.logout(); } } - return Response.ok().build(); + return newFileIdentifier; } + @PUT + @Path("{id}/rename") + public Response rename(@FormParam("newName") String newName){ + InnerMethodName.instance.set("rename"); + //TODO: check if identifier is The Workspace root, or the trash folder or the VREFolder root or if the item is thrashed + Session ses = null; + + try{ + final String login = AuthorizationProvider.instance.get().getClient().getId(); + + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + authChecker.checkWriteAuthorizationControl(ses, id, false); + + final Node nodeToMove = ses.getNodeByIdentifier(id); + + final Item item = node2Item.getItem(nodeToMove, null); + + if (item instanceof SharedFolder) + throw new InvalidItemException("shared folder"); + + if (Constants.FOLDERS_TO_EXLUDE.contains(item.getTitle())) + throw new InvalidItemException("protected folder cannot be renamed"); + + + ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login); + ses.getWorkspace().getLockManager().lock(nodeToMove.getParent().getPath(), false, true, 0,login); + try { + String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, nodeToMove.getParent(), newName); + + String newPath = String.format("%s/%s", nodeToMove.getParent().getPath(), uniqueName); + nodeToMove.setProperty(NodeProperty.TITLE.toString(), uniqueName); + Utils.setPropertyOnChangeNode(nodeToMove, login, ItemAction.RENAMED); + ses.move(nodeToMove.getPath(), newPath); + accountingHandler.createRename(item.getTitle(), uniqueName, ses.getNode(newPath), ses, false); + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath()); + ses.getWorkspace().getLockManager().unlock(nodeToMove.getParent().getPath()); + } + + }catch(RepositoryException re ){ + log.error("jcr error moving item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error moving item", she); + GXOutboundErrorResponse.throwException(she); + } finally{ + if (ses!=null) { + ses.logout(); + } + + } + return Response.ok(id).build(); + } + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{id}/metadata") + public Response setProperties(org.gcube.common.storagehub.model.Metadata metadata){ + InnerMethodName.instance.set("updateMetadata"); + + Session ses = null; + + try{ + final String login = AuthorizationProvider.instance.get().getClient().getId(); + + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + authChecker.checkWriteAuthorizationControl(ses, id, false); + + final Node nodeToUpdate = ses.getNodeByIdentifier(id); + + + ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,login); + try { + item2Node.updateMetadataNode(ses, nodeToUpdate, metadata.getValues(), login); + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(nodeToUpdate.getPath()); + } + //TODO: UPDATE accounting + + }catch(RepositoryException re ){ + log.error("jcr error moving item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error moving item", she); + GXOutboundErrorResponse.throwException(she); + } finally{ + if (ses!=null) { + ses.logout(); + } + + } + return Response.ok(id).build(); + } + + + @DELETE @Path("{id}") - public Response deleteItem(@PathParam("id") String identifier){ + public Response deleteItem(){ InnerMethodName.instance.set("deleteItem"); //TODO: check if identifier is The Workspace root, or the trash folder or the VREFolder root //TODO: check also that is not already trashed Session ses = null; try{ - log.info("removing node with id {}", identifier); - + log.info("removing node with id {}", id); + //TODO check if it is possible to change all the ACL on a workspace ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - authChecker.checkWriteAuthorizationControl(ses, identifier, false); + authChecker.checkWriteAuthorizationControl(ses, id, false); - final Node nodeToDelete = ses.getNodeByIdentifier(identifier); + final Node nodeToDelete = ses.getNodeByIdentifier(id); + + Item itemToDelete = node2Item.getItem(nodeToDelete, Excludes.GET_ONLY_CONTENT); + + if (itemToDelete instanceof SharedFolder || itemToDelete instanceof VreFolder || (itemToDelete instanceof FolderItem && Utils.hasSharedChildren(nodeToDelete))) + throw new InvalidItemException("SharedFolder, VreFolder or folders with shared children cannot be deleted"); - Item itemToDelete = ItemHandler.getItem(nodeToDelete, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME, NodeConstants.OWNER_NAME)); - - if (itemToDelete instanceof SharedFolder || itemToDelete instanceof VreFolder || (itemToDelete instanceof FolderItem && Utils.hasSharedChildren((FolderItem) itemToDelete, ses))) - throw new Exception("SharedFolder, VreFolder or folders with shared children cannot be deleted"); - log.debug("item is trashed? {}", itemToDelete.isTrashed()); - - if (!itemToDelete.isTrashed()) - moveToTrash(ses, nodeToDelete, itemToDelete); - else - removeNode(ses, itemToDelete); - }catch(Exception e){ - log.error("error removing item with id {} in Thrash",identifier,e); - throw new WebApplicationException(e); + if (!itemToDelete.isTrashed()) + trashHandler.moveToTrash(ses, nodeToDelete, itemToDelete); + else + trashHandler.removeNodes(ses, Collections.singletonList(itemToDelete)); + + }catch(RepositoryException re ){ + log.error("jcr error moving item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error moving item", she); + GXOutboundErrorResponse.throwException(she); } finally{ if (ses!=null) { ses.logout(); @@ -463,124 +828,5 @@ public class ItemsManager { return Response.ok().build(); } - - private void removeNode(Session ses, Item itemToDelete) throws Exception{ - log.debug("removing node"); - final String login = AuthorizationProvider.instance.get().getClient().getId(); - String parentPath = itemToDelete.getParentPath(); - try { - ses.getWorkspace().getLockManager().lock(parentPath, true, true, 0,login); - Set idsToDelete = new HashSet<>(); - getAllContentIds(ses, idsToDelete, itemToDelete); - ses.removeItem(itemToDelete.getPath()); - new Thread() { - - private String user = AuthorizationProvider.instance.get().getClient().getId(); - - public void run() { - for (String id: idsToDelete) { - try { - IClient client = Utils.getStorageClient(user).getClient(); - client.remove().RFileById(id); - log.debug("file with id {} correctly removed on storage",id); - }catch(Throwable t) { - log.warn("error removing file on storage with id {}",id, t); - } - } - - } - }.start();; - ses.save(); - }finally { - ses.getWorkspace().getLockManager().unlock(parentPath); - } - } - - - private void getAllContentIds(Session ses, Set idsToDelete, Item itemToDelete) throws Exception{ - if (itemToDelete instanceof AbstractFileItem) { - List versions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(itemToDelete.getId()), ses); - - versions.forEach(v -> { - try { - String storageId =v.getProperty("hl:storageId").toString(); - idsToDelete.add(storageId); - log.info("retrieved StorageId {} for version {}", storageId, v.getName()); - } catch (Exception e) { - log.warn("error retreiving sotrageId",e); - } - }); - - idsToDelete.add(((AbstractFileItem) itemToDelete).getContent().getStorageId()); - }else if (itemToDelete instanceof FolderItem) { - List items = Utils.getItemList(ses.getNodeByIdentifier(itemToDelete.getId()), Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME, NodeConstants.OWNER_NAME) , null, true); - for (Item item: items) - getAllContentIds(ses, idsToDelete, item); - - } - - } - - - private void moveToTrash(Session ses, Node nodeToDelete, Item item) throws Exception{ - log.debug("moving node to trash"); - final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); - - final String login = AuthorizationProvider.instance.get().getClient().getId(); - - 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(AuthorizationProvider.instance.get().getClient().getId()); - 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(pathUUid); - trashItem.setName(pathUUid); - trashItem.setOriginalParentId(nodeToDelete.getParent().getIdentifier()); - - trashItem.setOwner(item.getOwner()); - 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; - trashItem.setMimeType(file.getContent().getMimeType()); - trashItem.setLenght(file.getContent().getSize()); - } - - log.debug("creating node"); - - Node newTrashItemNode = ItemHandler.createNodeFromItem(ses, trashFolder, trashItem); - - ses.save(); - log.debug("calling move into jcr"); - ses.getWorkspace().move(nodeToDelete.getPath(), Paths.append(Paths.getPath(newTrashItemNode.getPath()),nodeToDelete.getName()).toPath()); - String mimetype = null; - if (item instanceof AbstractFileItem) - mimetype = ((AbstractFileItem) item).getContent().getMimeType(); - accountingHandler.createFolderRemoveObj(item.getName(), item.getClass().getSimpleName(), mimetype, ses, ses.getNodeByIdentifier(item.getParentId()), true); - }finally { - ses.getWorkspace().getLockManager().unlock(nodeToDelete.getPath()); - ses.getWorkspace().getLockManager().unlock(trashFolder.getPath()); - } - - } - - } \ No newline at end of file diff --git a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java index cccb966..bbe4fc7 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java @@ -1,5 +1,6 @@ package org.gcube.data.access.storagehub.services; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,27 +10,35 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import javax.servlet.ServletContext; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.xml.ws.WebServiceException; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.common.scope.impl.ScopeBean; import org.gcube.common.scope.impl.ScopeBean.Type; +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.InvalidItemException; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; import org.gcube.common.storagehub.model.expressions.Expression; import org.gcube.common.storagehub.model.expressions.logical.And; import org.gcube.common.storagehub.model.expressions.logical.ISDescendant; import org.gcube.common.storagehub.model.items.Item; +import org.gcube.common.storagehub.model.items.TrashItem; import org.gcube.common.storagehub.model.service.ItemList; import org.gcube.common.storagehub.model.service.ItemWrapper; import org.gcube.data.access.storagehub.AuthorizationChecker; @@ -37,7 +46,9 @@ import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Range; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.handlers.CredentialHandler; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; +import org.gcube.data.access.storagehub.handlers.TrashHandler; import org.gcube.data.access.storagehub.handlers.VRE; import org.gcube.data.access.storagehub.handlers.VREManager; import org.gcube.data.access.storagehub.query.sql2.evaluators.Evaluators; @@ -69,10 +80,15 @@ public class WorkspaceManager { @Inject VREManager vreManager; + @Inject + TrashHandler trashHandler; + @RequestScoped @QueryParam("exclude") private List excludes = Collections.emptyList(); + @Inject Node2ItemConverter node2Item; + @Inject Item2NodeConverter item2Node; @Path("") @GET @@ -87,16 +103,18 @@ public class WorkspaceManager { Item toReturn = null; try{ - String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); log.trace("time to connect to repo {}",(System.currentTimeMillis()-start)); Node node = ses.getNode(absolutePath.toPath()); authChecker.checkReadAuthorizationControl(ses, node.getIdentifier()); - toReturn = ItemHandler.getItem(node, excludes); - }catch(Throwable e){ - log.error("error reading the node children of {}",absolutePath,e); - throw new WebApplicationException("error getting WS folder "+absolutePath.toPath(),e) ; + toReturn = node2Item.getItem(node, excludes); + }catch(RepositoryException re ){ + log.error("jcr error getting workspace item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting workspace item", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -105,10 +123,10 @@ public class WorkspaceManager { return new ItemWrapper(toReturn); } - private synchronized VRE getVreFolderItem(Session ses) throws Exception{ + private synchronized VRE getVreFolderItem(Session ses) throws RepositoryException, BackendGenericError{ org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); ScopeBean bean = new ScopeBean(ScopeProvider.instance.get()); - if (!bean.is(Type.VRE)) throw new Exception("the current scope is not a VRE"); + if (!bean.is(Type.VRE)) throw new BackendGenericError("the current scope is not a VRE"); String entireScopeName= bean.toString().replaceAll("^/(.*)/?$", "$1").replaceAll("/", "-"); VRE vre = vreManager.getVRE(entireScopeName); if (vre!=null) return vre; @@ -117,10 +135,10 @@ public class WorkspaceManager { Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE); NodeIterator it = jcrQuery.execute().getNodes(); - if (!it.hasNext()) throw new Exception("vre folder not found for context "+entireScopeName); + if (!it.hasNext()) throw new BackendGenericError("vre folder not found for context "+entireScopeName); Node folder = it.nextNode(); - Item vreFolder = ItemHandler.getItem(folder, excludes); + Item vreFolder = node2Item.getItem(folder, excludes); return vreManager.putVRE(vreFolder); } @@ -133,17 +151,21 @@ public class WorkspaceManager { public ItemWrapper getVreRootFolder(){ InnerMethodName.instance.set("getVreRootFolder"); Session ses = null; + Item vreItem = null; try { - String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - return new ItemWrapper(getVreFolderItem(ses).getVreFolder()); - }catch(Throwable e){ - log.error("error reading vreNode for context {}",ScopeProvider.instance.get(),e); - throw new WebApplicationException("error retrieving vre folder",e); + vreItem = getVreFolderItem(ses).getVreFolder(); + }catch(RepositoryException re ){ + log.error("jcr error getting vrefolder", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting vrefolder", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); - } + } + return new ItemWrapper(vreItem); } @Path("vrefolder/recents") @@ -152,23 +174,30 @@ public class WorkspaceManager { public ItemList getVreFolderRecentsDocument(){ InnerMethodName.instance.set("getVreFolderRecents"); Session ses = null; - + List recentItems = Collections.emptyList(); try{ String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); VRE vre = getVreFolderItem(ses); log.trace("VRE retrieved {}",vre.getVreFolder().getTitle()); - List recentItems = vre.getRecents(); + recentItems = vre.getRecents(); log.trace("recents retrieved {}",vre.getVreFolder().getTitle()); return new ItemList(recentItems); - }catch(Throwable e){ - log.error("error reading recents for context {}",ScopeProvider.instance.get(),e); - throw new WebApplicationException("error reading recents",e); + }catch(RepositoryException re ){ + log.error("jcr error getting recents", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting recents", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } + + return new ItemList(recentItems); + + } @@ -180,29 +209,98 @@ public class WorkspaceManager { Session ses = null; org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getHomePath(), Constants.TRASH_ROOT_FOLDER_NAME); - + Item item = null; try{ - String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); - - Node folder = ses.getNode(trashPath.toPath()); - Item item = ItemHandler.getItem(folder, excludes); - - return new ItemWrapper(item); - }catch(Throwable e){ - log.error("error reading the node {}",trashPath,e); - throw new WebApplicationException("error retrieving trash folder",e); + item = node2Item.getItem(folder, excludes); + }catch(RepositoryException re ){ + log.error("jcr error getting trash", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting trash", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); } + + return new ItemWrapper(item); } + + @Path("trash/empty") + @DELETE + public String emptyTrash(){ + InnerMethodName.instance.set("emptyTrash"); + Session ses = null; + org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getHomePath(), Constants.TRASH_ROOT_FOLDER_NAME); + String toReturn = null; + try{ + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + Node trashNode = ses.getNode(trashPath.toPath()); + authChecker.checkWriteAuthorizationControl(ses, trashNode.getIdentifier(), false); + List itemsToDelete = Utils.getItemList(trashNode, Excludes.ALL, null, true, null); + trashHandler.removeNodes(ses, itemsToDelete); + toReturn = trashNode.getIdentifier(); + }catch(RepositoryException re ){ + log.error("jcr error emptying trash", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error emptying trash", she); + GXOutboundErrorResponse.throwException(she); + }finally{ + if (ses!=null) + ses.logout(); + } + + return toReturn; + } + + @PUT + @Consumes(MediaType.TEXT_PLAIN) + @Path("trash/restore") + public String restoreItem(String identifier){ + InnerMethodName.instance.set("restoreItem"); + Session ses = null; + String toReturn = null; + try{ + log.info("restoring node with id {}", identifier); + + //TODO check if it is possible to change all the ACL on a workspace + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + authChecker.checkWriteAuthorizationControl(ses, identifier, false); + + final Node nodeToRestore = ses.getNodeByIdentifier(identifier); + + Item itemToRestore = node2Item.getItem(nodeToRestore, Excludes.ALL); + + if (!(itemToRestore instanceof TrashItem)) + throw new InvalidItemException("Only trash items can be restored"); + + toReturn = trashHandler.restoreItem(ses, (TrashItem)itemToRestore); + + }catch(RepositoryException re ){ + log.error("error restoring item with id {}",identifier, re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error restoring item with id {}",identifier, she); + GXOutboundErrorResponse.throwException(she); + } finally{ + if (ses!=null) { + ses.logout(); + } + } + + return toReturn; + } + + @Path("vrefolders") @GET @Produces(MediaType.APPLICATION_JSON) @@ -213,12 +311,14 @@ public class WorkspaceManager { org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); List toReturn = null; try{ - String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, null, false); - }catch(Throwable e){ - log.error("error reading the node children of {}",vrePath,e); - throw new WebApplicationException("error reading the node children of "+vrePath.toPath(),e); + toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, null, false, null); + }catch(RepositoryException re ){ + log.error("error reading the node children of {}",vrePath, re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error reading the node children of {}",vrePath, she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -233,16 +333,17 @@ public class WorkspaceManager { public ItemList getVreFoldersPaged(@QueryParam("start") Integer start, @QueryParam("limit") Integer limit){ InnerMethodName.instance.set("getVreFoldersPaged"); Session ses = null; - org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); List toReturn = null; try{ - String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, new Range(start, limit), false); - }catch(Throwable e){ - log.error("(paged) error reading the node children of {}",vrePath,e); - throw new WebApplicationException("error reading the node children of "+vrePath.toPath(),e); + toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, new Range(start, limit), false, null); + }catch(RepositoryException re ){ + log.error("(paged) error reading the node children of {}",vrePath, re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("(paged) error reading the node children of {}",vrePath, she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); @@ -266,13 +367,11 @@ public class WorkspaceManager { ObjectMapper mapper = new ObjectMapper(); Expression expression = mapper.readValue(jsonExpr, Expression.class); String stringExpression = evaluator.evaluate(new And(new ISDescendant(Utils.getHomePath()), expression)); - //ADD ALSO LIMIT AND OFFSET String orderBy = ""; if (orderField!=null && orderField.size()>0) orderBy= String.format("ORDER BY %s", orderField.stream().collect(Collectors.joining(",")).toString()); - String sql2Query = String.format("SELECT * FROM [%s] AS node WHERE %s %s ",node, stringExpression,orderBy); log.info("query sent is {}",sql2Query); @@ -293,12 +392,13 @@ public class WorkspaceManager { NodeIterator it = result.getNodes(); while (it.hasNext()) - toReturn.add(ItemHandler.getItem(it.nextNode(), null)); - - - }catch(Throwable e){ - log.error("error executing the query",e); - throw new WebServiceException("error executing the query", e); + toReturn.add(node2Item.getItem(it.nextNode(), null)); + }catch(RepositoryException | IOException re ){ + log.error("error executing the query", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error executing the query", she); + GXOutboundErrorResponse.throwException(she); }finally{ if (ses!=null) ses.logout(); diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 97fb7ac..2c5cc4b 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -8,7 +8,10 @@ admin-pwd gcube2010*onan - + + resolver-basepath + https://data1-d.d4science.net/shub + org.gcube.data.access.storagehub.StorageHub diff --git a/src/test/java/org/gcube/data/access/fs/Expressions.java b/src/test/java/org/gcube/data/access/fs/Expressions.java index 7e789d5..aed9094 100644 --- a/src/test/java/org/gcube/data/access/fs/Expressions.java +++ b/src/test/java/org/gcube/data/access/fs/Expressions.java @@ -23,6 +23,7 @@ public class Expressions { @Inject Evaluators evaluators; + @Test public void test() { diff --git a/src/test/java/org/gcube/data/access/fs/TestFields.java b/src/test/java/org/gcube/data/access/fs/TestFields.java index 0b4a552..745cb47 100644 --- a/src/test/java/org/gcube/data/access/fs/TestFields.java +++ b/src/test/java/org/gcube/data/access/fs/TestFields.java @@ -13,7 +13,7 @@ import javax.jcr.nodetype.NodeType; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.types.ItemAction; -import org.gcube.data.access.storagehub.handlers.ItemHandler; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; @@ -24,11 +24,6 @@ public class TestFields { Logger logger = LoggerFactory.getLogger(TestFields.class); - @Test - public void replace(){ - System.out.println("/Home/Giancarlo".replaceAll("^/(.*)/?$", "$1").replaceAll("/", "-")); - } - @Test public void iterateOverFields() throws Exception{ @@ -62,10 +57,10 @@ public class TestFields { when(node.getParent()).thenReturn(parent); when(node.getProperty(anyString())).thenReturn(prop); when(node.getNode(anyString())).thenReturn(node); - Item item = ItemHandler.getItem(node, Arrays.asList("hl:accounting","jcr:content")); + Item item = new Node2ItemConverter().getItem(node, Arrays.asList("hl:accounting","jcr:content")); Assert.assertTrue(item.isShared()); } - + } diff --git a/src/test/java/org/gcube/data/access/fs/TestNode.java b/src/test/java/org/gcube/data/access/fs/TestNode.java index d3a4fab..8f0c103 100644 --- a/src/test/java/org/gcube/data/access/fs/TestNode.java +++ b/src/test/java/org/gcube/data/access/fs/TestNode.java @@ -7,6 +7,8 @@ import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.io.File; import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.imageio.ImageIO; @@ -14,7 +16,7 @@ import org.junit.Test; public class TestNode { - @Test + /*@Test public void testShared() throws Exception{ BufferedImage buf = ImageIO.read(new File("/home/lucio/Downloads/djbattle.png")); @@ -32,7 +34,9 @@ public class TestNode { /*buffered.getGraphics().drawImage(image, 0, 0 , null); ImageIO.write(buffered, "png", buffer ); - byte[] imageInByte = buffer.toByteArray();*/ - } + byte[] imageInByte = buffer.toByteArray(); + }*/ + + } diff --git a/workspace-repository-devnext.d4science.org b/workspace-repository-devnext.d4science.org new file mode 100644 index 0000000..236507b Binary files /dev/null and b/workspace-repository-devnext.d4science.org differ