package org.gcube.data.access.storagehub.services; import java.io.InputStream; import java.util.Collections; import java.util.List; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import org.apache.jackrabbit.api.JackrabbitSession; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.Paths; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; import org.gcube.common.storagehub.model.exceptions.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.items.TrashItem; import org.gcube.common.storagehub.model.items.nodes.PayloadBackend; import org.gcube.common.storagehub.model.service.ItemList; import org.gcube.common.storagehub.model.service.ItemWrapper; import org.gcube.common.storagehub.model.storages.MetaInfo; import org.gcube.common.storagehub.model.storages.StorageBackend; import org.gcube.common.storagehub.model.storages.StorageBackendFactory; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.PathUtil; import org.gcube.data.access.storagehub.Range; import org.gcube.data.access.storagehub.StorageHubAppllicationManager; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.handlers.PublicLinkHandler; import org.gcube.data.access.storagehub.handlers.TrashHandler; import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter; import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter; import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters; import org.gcube.data.access.storagehub.handlers.plugins.StorageBackendHandler; import org.gcube.data.access.storagehub.handlers.vres.VRE; import org.gcube.data.access.storagehub.handlers.vres.VREManager; import org.gcube.data.access.storagehub.query.sql2.evaluators.Evaluators; import org.gcube.data.access.storagehub.storage.backend.impl.GCubeVolatileStorageBackendFactory; import org.gcube.smartgears.annotations.ManagedBy; import org.gcube.smartgears.utils.InnerMethodName; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webcohesion.enunciate.metadata.rs.RequestHeader; import com.webcohesion.enunciate.metadata.rs.RequestHeaders; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.servlet.ServletContext; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @Path("/") @ManagedBy(StorageHubAppllicationManager.class) @RequestHeaders({ @RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), }) public class WorkspaceManager extends Impersonable { private static final Logger log = LoggerFactory.getLogger(WorkspaceManager.class); RepositoryInitializer repository = StorageHubAppllicationManager.getRepository(); @Inject Evaluators evaluator; @Inject PathUtil pathUtil; @Inject VREManager vreManager; @Context ServletContext context; @Inject AuthorizationChecker authChecker; @Inject TrashHandler trashHandler; @Inject StorageBackendHandler storageBackendHandler; @Inject PublicLinkHandler publicLinkHandler; @RequestScoped @QueryParam("exclude") private List excludes = Collections.emptyList(); @Inject Node2ItemConverter node2Item; @Inject Item2NodeConverter item2Node; @Path("/") @GET @Produces(MediaType.APPLICATION_JSON) public ItemWrapper getWorkspace(@QueryParam("relPath") String relPath) { InnerMethodName.set("getWorkspace"); Session ses = null; org.gcube.common.storagehub.model.Path absolutePath; if (relPath == null) absolutePath = pathUtil.getWorkspacePath(currentUser); else absolutePath = Paths.append(pathUtil.getWorkspacePath(currentUser), relPath); Item toReturn = null; try { ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); // TODO: remove when all user will have TRASH org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses); if (!ses.nodeExists(trashPath.toPath())) { Node wsNode = ses.getNode(pathUtil.getWorkspacePath(currentUser).toPath()); FolderCreationParameters trashFolderParameters = FolderCreationParameters.builder() .name(Constants.TRASH_ROOT_FOLDER_NAME).description("trash of " + currentUser) .author(currentUser).on(wsNode.getIdentifier()).with(ses).build(); Utils.createFolderInternally(trashFolderParameters, null, true); ses.save(); } Node node = ses.getNode(absolutePath.toPath()); authChecker.checkReadAuthorizationControl(ses, currentUser, node.getIdentifier()); 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(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemWrapper(toReturn); } /** * Uploads a file in the volatile area returning a public link * * @param id * @param name * @param description * @param stream * @param fileDetail * @return */ @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("volatile") public String uploadVolatileFile(@FormDataParam("file") InputStream stream, @FormDataParam("file") FormDataContentDisposition fileDetail) { InnerMethodName.set("uploadToVolatileArea"); log.info("uploading file {} of size {} to volatile area ({} - {})", fileDetail.getFileName(), fileDetail.getSize(), fileDetail.getName(), fileDetail.getParameters().toString()); String toReturn = null; try { long size = fileDetail.getSize(); PayloadBackend payloadBackend = new PayloadBackend(GCubeVolatileStorageBackendFactory.NAME, null); StorageBackendFactory sbf = storageBackendHandler.get(payloadBackend); StorageBackend sb = sbf.create(payloadBackend); log.info("UPLOAD: call started with file size {}", size); MetaInfo info = sb.upload(stream, null, fileDetail.getFileName(), currentUser); log.debug("UPLOAD: call finished"); toReturn = publicLinkHandler.getForVolatile(info.getStorageId(), GCubeVolatileStorageBackendFactory.NAME, context); } catch (StorageHubException se) { log.error("error uploading file to volatile area", se); GXOutboundErrorResponse.throwException(se); } catch (Throwable e) { log.error("error uploading file to volatile area", e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } return toReturn; } @Path("vrefolder") @GET @Produces(MediaType.APPLICATION_JSON) public ItemWrapper getVreRootFolder() { InnerMethodName.set("getVreRootFolder"); JackrabbitSession ses = null; Item vreItem = null; try { ses = (JackrabbitSession) repository.getRepository().login(Constants.JCR_CREDENTIALS); vreItem = vreManager.getVreFolderItem(ses, currentUser, excludes).getVreFolder(); } catch (RepositoryException re) { log.error("jcr error getting vrefolder", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); } catch (StorageHubException she) { log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemWrapper(vreItem); } @Path("vrefolder/recents") @GET @Produces(MediaType.APPLICATION_JSON) public ItemList getVreFolderRecentsDocument() { InnerMethodName.set("getVreFolderRecents"); JackrabbitSession ses = null; List recentItems = Collections.emptyList(); try { ses = (JackrabbitSession) repository.getRepository().login(Constants.JCR_CREDENTIALS); VRE vre = vreManager.getVreFolderItem(ses, currentUser, excludes); log.trace("VRE retrieved {}", vre.getVreFolder().getTitle()); recentItems = vre.getRecents(); log.trace("recents retrieved {}", vre.getVreFolder().getTitle()); return new ItemList(recentItems); } catch (RepositoryException re) { log.error("jcr error getting recents", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); } catch (StorageHubException she) { log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemList(recentItems); } @Path("trash") @GET @Produces(MediaType.APPLICATION_JSON) public ItemWrapper getTrashRootFolder() { InnerMethodName.set("getTrashRootFolder"); Session ses = null; Item item = null; try { long start = System.currentTimeMillis(); ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses); log.info("time to connect to repo {}", (System.currentTimeMillis() - start)); Node folder = ses.getNode(trashPath.toPath()); 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(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemWrapper(item); } @Path("trash/empty") @DELETE public String emptyTrash() { InnerMethodName.set("emptyTrash"); Session ses = null; String toReturn = null; try { ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses); Node trashNode = ses.getNode(trashPath.toPath()); 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(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return toReturn; } @PUT @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Path("trash/restore") public String restoreItem(@FormParam("trashedItemId") String trashedItemId, @FormParam("destinationId") String destinationFolderId) { InnerMethodName.set("restoreItem"); Session ses = null; String toReturn = null; try { log.info("restoring node with id {}", trashedItemId); ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); final Node nodeToRestore = ses.getNodeByIdentifier(trashedItemId); Item itemToRestore = node2Item.getItem(nodeToRestore, Excludes.ALL); if (!(itemToRestore instanceof TrashItem)) throw new InvalidItemException("Only trash items can be restored"); org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses); if (!itemToRestore.getPath().startsWith(trashPath.toPath())) throw new UserNotAuthorizedException("this item is not in the user " + currentUser + " trash"); Item destinationItem = null; if (destinationFolderId != null) { destinationItem = node2Item.getItem(ses.getNodeByIdentifier(destinationFolderId), Excludes.ALL); if (!(destinationItem instanceof FolderItem)) throw new InvalidCallParameters("destintation item is not a folder"); toReturn = trashHandler.restoreItem(ses, (TrashItem) itemToRestore, (FolderItem) destinationItem, currentUser); } else toReturn = trashHandler.restoreItem(ses, (TrashItem) itemToRestore, null, currentUser); } catch (RepositoryException re) { log.error("error restoring item with id {}", trashedItemId, re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); } catch (StorageHubException she) { log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) { ses.logout(); } } return toReturn; } @Path("vrefolders") @GET @Produces(MediaType.APPLICATION_JSON) public ItemList getVreFolders() { InnerMethodName.set("getVreFolders"); Session ses = null; List toReturn = null; org.gcube.common.storagehub.model.Path vrePath = null; try { ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); vrePath = pathUtil.getVREsPath(currentUser, ses); log.info("vres folder path is {}", vrePath.toPath()); 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(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemList(toReturn); } @Path("vrefolders/paged") @GET @Produces(MediaType.APPLICATION_JSON) public ItemList getVreFoldersPaged(@QueryParam("start") Integer start, @QueryParam("limit") Integer limit) { InnerMethodName.set("getVreFoldersPaged"); Session ses = null; org.gcube.common.storagehub.model.Path vrePath = null; List toReturn = null; try { ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); vrePath = pathUtil.getVREsPath(currentUser, ses); 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(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemList(toReturn); } /* * @Path("shared-by-me") * * @GET * * @Produces(MediaType.APPLICATION_JSON) public ItemList getMySharedFolders(){ * InnerMethodName.set("getMySharedFolders"); Session ses = null; List toReturn = null; org.gcube.common.storagehub.model.Path sharedPath = * null; try{ ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); * sharedPath = pathUtil.getMySharedPath(currentUser); * log.info("my shared folder path is folder path is {}",sharedPath.toPath()); * * toReturn = Utils.getItemList(ses.getNode(sharedPath.toPath()) , excludes, * null, false, SharedFolder.class); }catch(RepositoryException re ){ * log.error("error reading my shared folder ({})",sharedPath, re); * GXOutboundErrorResponse.throwException(new BackendGenericError(re)); * }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she); * GXOutboundErrorResponse.throwException(she, * Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null) * ses.logout(); } * * return new ItemList(toReturn); } */ @Path("shared-with-me") @GET @Produces(MediaType.APPLICATION_JSON) public ItemList getSharedWithMeFolders() { InnerMethodName.set("getSharedWithMeFolders"); Session ses = null; List toReturn = null; org.gcube.common.storagehub.model.Path sharedPath = null; try { ses = repository.getRepository().login(Constants.JCR_CREDENTIALS); sharedPath = pathUtil.getSharedWithMePath(currentUser); log.info("vres folder path is {}", sharedPath.toPath()); toReturn = Utils.getItemList(ses.getNode(sharedPath.toPath()), excludes, null, false, SharedFolder.class); } catch (RepositoryException re) { log.error("error reading shared with me folder ({})", sharedPath, re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); } catch (StorageHubException she) { log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); } finally { if (ses != null) ses.logout(); } return new ItemList(toReturn); } @Path("count") @GET public String getTotalItemsCount() { InnerMethodName.set("getTotalItemsCount"); return "1203"; } @Path("size") @GET public String getTotalVolume() { InnerMethodName.set("getTotalSize"); return "120300000"; } }