From 396f57cdb7f298865c0e39d7da3700725fc1526a Mon Sep 17 00:00:00 2001 From: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com> Date: Wed, 27 Sep 2023 13:43:57 +0200 Subject: [PATCH 01/10] lock/download-preview-temp --- .../main/java/org/dspace/util/FileInfo.java | 40 ++ .../dspace/util/FileTreeViewGenerator.java | 89 ++++ .../app/rest/MetadataBitstreamController.java | 188 +++++++++ .../MetadataBitstreamWrapperConverter.java | 52 +++ .../model/MetadataBitstreamWrapperRest.java | 123 ++++++ .../MetadataBitstreamWrapperResource.java | 19 + .../wrapper/MetadataBitstreamWrapper.java | 74 ++++ .../MetadataBitstreamRestRepository.java | 395 ++++++++++++++++++ .../rest/MetadataBitstreamControllerIT.java | 131 ++++++ .../MetadataBitstreamRestRepositoryIT.java | 192 +++++++++ 10 files changed, 1303 insertions(+) create mode 100644 dspace-api/src/main/java/org/dspace/util/FileInfo.java create mode 100644 dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java new file mode 100644 index 000000000000..4bc33f79449c --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -0,0 +1,40 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.util; + +import java.util.Hashtable; +/** + * This class is used to store the information about a file or a directory + * + * @author longtv + */ +public class FileInfo { + + public String name; + public String content; + public String size; + public boolean isDirectory; + + public Hashtable sub = null; + + public FileInfo(String name) { + this.name = name; + sub = new Hashtable(); + isDirectory = true; + } + public FileInfo(String content, boolean isDirectory) { + this.content = content; + this.isDirectory = isDirectory; + } + + public FileInfo(String name, String size) { + this.name = name; + this.size = size; + isDirectory = false; + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java new file mode 100644 index 000000000000..4a74eb7b2b60 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -0,0 +1,89 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.util; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +/** + * Generate a tree view of the file in a bitstream + * + * @author longtv + */ +public class FileTreeViewGenerator { + private FileTreeViewGenerator () { + } + + public static List parse(String data) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(new StringReader(data))); + Element rootElement = document.getDocumentElement(); + NodeList nl = rootElement.getChildNodes(); + FileInfo root = new FileInfo("root"); + int limitFile = 100; + int countFile = 0; + Node n = nl.item(0); + do { + String fileInfo = n.getFirstChild().getTextContent(); + String f[] = fileInfo.split("\\|"); + String fileName = ""; + String path = f[0]; + long size = Long.parseLong(f[1]); + if (!path.endsWith("/")) { + fileName = path.substring(path.lastIndexOf('/') + 1); + if (path.lastIndexOf('/') != -1) { + path = path.substring(0, path.lastIndexOf('/')); + } else { + path = ""; + } + } + FileInfo current = root; + for (String p : path.split("/")) { + if (current.sub.containsKey(p)) { + current = current.sub.get(p); + } else { + FileInfo temp = new FileInfo(p); + current.sub.put(p, temp); + current = temp; + } + } + if (!fileName.isEmpty()) { + FileInfo temp = new FileInfo(fileName, humanReadableFileSize(size)); + current.sub.put(fileName, temp); + countFile++; + } + } while ((n = n.getNextSibling()) != null && countFile < limitFile); + return new ArrayList<>(root.sub.values()); + } + public static String humanReadableFileSize(long bytes) { + int thresh = 1024; + if (Math.abs(bytes) < thresh) { + return bytes + " B"; + } + String units[] = {"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int u = -1; + do { + bytes /= thresh; + ++u; + } while (Math.abs(bytes) >= thresh && u < units.length - 1); + return bytes + " " + units[u]; + } +} + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java new file mode 100644 index 000000000000..f9d35517456c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -0,0 +1,188 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + + +import org.apache.commons.compress.archivers.zip.Zip64Mode; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.MetadataBitstreamWrapper; +import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.MissingLicenseAgreementException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.*; +import org.dspace.content.service.BitstreamService; +import org.dspace.core.Context; +import org.dspace.handle.service.HandleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.dspace.core.Constants; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.Deflater; +import java.util.zip.ZipOutputStream; + +@RestController +@RequestMapping("/bitstream") +public class MetadataBitstreamController { + + @Autowired + BitstreamService bitstreamService; + + @Autowired + HandleService handleService; + @Autowired + private AuthorizeService authorizeService; + @GetMapping("/handle/{id}/{subId}/{fileName}") + public ResponseEntity downloadSingleFile( + @PathVariable("id") String id, + @PathVariable("subId") String subId, + @PathVariable("fileName") String fileName, + HttpServletRequest request, HttpServletResponse response) throws IOException { + String handleID = id + "/" + subId; + if (StringUtils.isBlank(handleID)) { + throw new DSpaceBadRequestException("handle cannot be null!"); + } + Context context = ContextUtil.obtainContext(request); + if (Objects.isNull(context)) { + throw new RuntimeException("Cannot obtain the context from the request."); + } + + DSpaceObject dso = null; + + try{ + dso = handleService.resolveToObject(context, handleID); + } catch (Exception e) { + throw new RuntimeException("Cannot resolve handle: " + handleID); + } + + if (dso != null && dso instanceof Item) { + Item item = (Item) dso; + List bundles = item.getBundles(); + for (Bundle bundle: + bundles) { + for (Bitstream bitstream: + bundle.getBitstreams()) { + try { + authorizeService.authorizeAction(context, bitstream, Constants.READ); + } catch (MissingLicenseAgreementException e) { + response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); + } catch (AuthorizeException e) { + response.sendRedirect("http://localhost:4000" + "/login"); + } catch (SQLException e) { + response.sendRedirect("http://localhost:4000" + "/login"); + } + String btName = bitstream.getName(); + if (btName.equalsIgnoreCase(fileName)) { + try { + BitstreamFormat bitstreamFormat = bitstream.getFormat(context); + if (bitstreamFormat == null || bitstreamFormat.getExtensions() == null || bitstreamFormat.getExtensions().size() == 0) { +// throw new RuntimeException("Cannot find the bitstream format."); + } + InputStream inputStream = bitstreamService.retrieve(context, bitstream); + InputStreamResource resource = new InputStreamResource(inputStream); + HttpHeaders header = new HttpHeaders(); + header.add(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=" + fileName); +// "attachment; filename=" + fileName +".pdf"); + header.add("Cache-Control", "no-cache, no-store, must-revalidate"); + header.add("Pragma", "no-cache"); + header.add("Expires", "0"); + return ResponseEntity.ok() + .headers(header) + .contentLength(inputStream.available()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + } + + return null; + } + + + @GetMapping("/allzip") + public void downloadFileZip(@RequestParam("handleId") String handleId, + HttpServletResponse response, + HttpServletRequest request) throws IOException, SQLException, AuthorizeException { + //TODO handle authorization + if (StringUtils.isBlank(handleId)) { + throw new DSpaceBadRequestException("handle cannot be null!"); + } + Context context = ContextUtil.obtainContext(request); + if (Objects.isNull(context)) { + throw new RuntimeException("Cannot obtain the context from the request."); + } + + DSpaceObject dso = null; + String name = ""; + try{ + dso = handleService.resolveToObject(context, handleId); + } catch (Exception e) { + throw new RuntimeException("Cannot resolve handle: " + handleId); + } + + if (dso != null && dso instanceof Item) { + Item item = (Item) dso; + name = item.getName() + ".zip"; + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", name)); + response.setContentType("application/zip"); + List bundles = item.getBundles("ORIGINAL"); + + + ZipArchiveOutputStream zip = new ZipArchiveOutputStream(response.getOutputStream()); + zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); + zip.setLevel(Deflater.NO_COMPRESSION); + for (Bundle original : bundles) { + List bss = original.getBitstreams(); + for (Bitstream bitstream : bss) { + try { + authorizeService.authorizeAction(context, bitstream, Constants.READ); + } catch (AuthorizeException e) { + response.sendRedirect("http://localhost:4000" + "/login"); + } catch (SQLException e) { + response.sendRedirect("http://localhost:4000" + "/login"); + } + String filename = bitstream.getName(); + ZipArchiveEntry ze = new ZipArchiveEntry(filename); + zip.putArchiveEntry(ze); + InputStream is = bitstreamService.retrieve(context, bitstream); + IOUtils.copy(is, zip); + zip.closeArchiveEntry(); + is.close(); + } + } + zip.close(); + response.getOutputStream().flush(); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java new file mode 100644 index 000000000000..50007f3dfe6f --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -0,0 +1,52 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.converter; + +import org.dspace.app.rest.model.MetadataBitstreamWrapper; +import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.rest.model.MetadataValueWrapper; +import org.dspace.app.rest.model.MetadataValueWrapperRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.core.Context; +import org.dspace.util.FileTreeViewGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +@Component +public class MetadataBitstreamWrapperConverter implements DSpaceConverter { + + @Lazy + @Autowired + private ConverterService converter; + + + @Autowired + private BitstreamConverter bitstreamConverter; + + @Override + public MetadataBitstreamWrapperRest convert(MetadataBitstreamWrapper modelObject, Projection projection) { + MetadataBitstreamWrapperRest bitstreamWrapperRest = new MetadataBitstreamWrapperRest(); + bitstreamWrapperRest.setProjection(projection); + bitstreamWrapperRest.setName(modelObject.getBitstream().getName()); + bitstreamWrapperRest.setId(modelObject.getBitstream().getID().toString()); + bitstreamWrapperRest.setDescription(modelObject.getDescription()); + bitstreamWrapperRest.setChecksum(modelObject.getBitstream().getChecksum()); + bitstreamWrapperRest.setFileSize(FileTreeViewGenerator.humanReadableFileSize(modelObject.getBitstream().getSizeBytes())); + bitstreamWrapperRest.setFileInfo(modelObject.getFileInfo()); + bitstreamWrapperRest.setHref(modelObject.getHref()); + bitstreamWrapperRest.setFormat(modelObject.getFormat()); + bitstreamWrapperRest.setCanPreview(modelObject.isCanPreview()); + return bitstreamWrapperRest; + } + + @Override + public Class getModelClass() { + return MetadataBitstreamWrapper.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java new file mode 100644 index 000000000000..e2a011c4f770 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -0,0 +1,123 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model; + +import org.dspace.app.rest.RestResourceController; +import org.dspace.util.FileInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class MetadataBitstreamWrapperRest extends BaseObjectRest{ + public static final String NAME = "metadatabitstream"; + public static final String CATEGORY = RestAddressableModel.CORE; + + private String name; + private String description; + private String fileSize; + private String checksum; + private List fileInfo; + private String format; + private String href; + private boolean canPreview; + + public MetadataBitstreamWrapperRest(String name, String description, String fileSize, String checksum, List fileInfo, String format, String href, boolean canPreview) { + this.name = name; + this.description = description; + this.fileSize = fileSize; + this.checksum = checksum; + this.fileInfo = fileInfo; + this.format = format; + this.href = href; + this.canPreview = canPreview; + } + + public void setCanPreview(boolean canPreview) { + this.canPreview = canPreview; + } + + public boolean isCanPreview() { + return canPreview; + } + + public MetadataBitstreamWrapperRest() { + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public List getFileInfo() { + return fileInfo; + } + + public void setFileInfo(List fileInfo) { + this.fileInfo = fileInfo; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + + public String getChecksum() { + return checksum; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java new file mode 100644 index 000000000000..1c09c9d95f5a --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java @@ -0,0 +1,19 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model.hateoas; + +import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +@RelNameDSpaceResource(MetadataBitstreamWrapperRest.NAME) +public class MetadataBitstreamWrapperResource extends DSpaceResource{ + public MetadataBitstreamWrapperResource(MetadataBitstreamWrapperRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java new file mode 100644 index 000000000000..0c8b684162f1 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java @@ -0,0 +1,74 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model; + +import org.dspace.content.Bitstream; +import org.dspace.util.FileInfo; + +import java.util.List; + +public class MetadataBitstreamWrapper { + private Bitstream bitstream; + private List fileInfo; + private String format; + private String description; + private String href; + + private boolean canPreview; + public MetadataBitstreamWrapper() { + } + + public MetadataBitstreamWrapper(Bitstream bitstream, List fileInfo, String format, String description, String href, boolean canPreview) { + this.bitstream = bitstream; + this.fileInfo = fileInfo; + this.format = format; + this.description = description; + this.href = href; + this.canPreview = canPreview; + } + + public String getDescription() { + return description; + } + + public boolean isCanPreview() { + return canPreview; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public Bitstream getBitstream() { + return bitstream; + } + + public void setBitstream(Bitstream bitstream) { + this.bitstream = bitstream; + } + + public List getFileInfo() { + return fileInfo; + } + + public void setFileInfo(List fileInfo) { + this.fileInfo = fileInfo; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java new file mode 100644 index 000000000000..bee16f565b27 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -0,0 +1,395 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.Parameter; +import org.dspace.app.rest.SearchRestMethod; +import org.dspace.app.rest.converter.BitstreamConverter; +import org.dspace.app.rest.converter.MetadataBitstreamWrapperConverter; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.MetadataBitstreamWrapper; +import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.rest.model.MetadataValueWrapper; +import org.dspace.app.util.Util; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.*; +import org.dspace.content.clarin.ClarinLicense; +import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.service.BitstreamService; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.handle.service.HandleService; +import org.dspace.util.FileInfo; +import org.dspace.util.FileTreeViewGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.*; +import java.nio.file.*; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +@Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) +public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); + @Autowired + HandleService handleService; + + @Autowired + BitstreamConverter bitstreamConverter; + + @Autowired + MetadataBitstreamWrapperConverter metadataBitstreamWrapperConverter; + @Autowired + ItemService itemService; + @Autowired + ClarinLicenseResourceMappingService licenseService; + + @Autowired + AuthorizeService authorizeService; + + @Autowired + BitstreamService bitstreamService; + + @SearchRestMethod(name = "byHandle") + public Page findByHandle(@Parameter(value = "handle", required = true) String handle, + @Parameter(value = "fileGrpType", required = false) String fileGrpType, + Pageable pageable) + throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException { + if (StringUtils.isBlank(handle)) { + throw new DSpaceBadRequestException("handle cannot be null!"); + } + List metadataValueWrappers = new ArrayList<>(); + Context context = obtainContext(); + if (Objects.isNull(context)) { + throw new RuntimeException("Cannot obtain the context from the request."); + } + HttpServletRequest request = getRequestService().getCurrentRequest().getHttpServletRequest(); + String contextPath = request.getContextPath(); + List rs = new ArrayList<>(); + DSpaceObject dso = null; + + try{ + dso = handleService.resolveToObject(context, handle); + } catch (Exception e) { + throw new RuntimeException("Cannot resolve handle: " + handle); + } + + if ( dso instanceof Item) { + Item item = (Item) dso; + List fileGrpTypes = Arrays.asList(fileGrpType.split(",")); + List bundles = findEnabledBundles(fileGrpTypes, item); + + for (Bundle bundle : + bundles) { + List bitstreams = new ArrayList<>(); + String use = bundle.getName(); + if ("THUMBNAIL".equals(use)) + { + Thumbnail thumbnail = itemService.getThumbnail(context, item, false); + if(thumbnail != null) { + bitstreams.add(thumbnail.getThumb()); + } + } + else + { + bitstreams = bundle.getBitstreams(); + } + + for (Bitstream bitstream : + bitstreams) { + List clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); + boolean canPreview = false; + if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { + ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); + ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); + canPreview = clarinLicense.getClarinLicenseLabels().stream().anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); + } + String identifier = null; + if (item != null && item.getHandle() != null) + { + identifier = "handle/" + item.getHandle(); + } + else if (item != null) + { + identifier = "item/" + item.getID(); + } + else + { + identifier = "id/" + bitstream.getID(); + } + String url = contextPath + "/bitstream/"+identifier; + try + { + if (bitstream.getName() != null) + { + url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); + } + } + catch (UnsupportedEncodingException uee) + { + log.error("UnsupportedEncodingException", uee); + } + + url += "?sequence="+bitstream.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; + if (canPreview) { + List metadataValues = bitstream.getMetadata(); + // Filter out all metadata values that are not local to the bitstream + // Uncomment this if we want to show metadata values that are local to the bitstream +// metadataValues = metadataValues.stream().filter(metadataValue -> +// match("local", "bitstream", "file", metadataValue.getMetadataField())) +// .collect(Collectors.toList()); + List fileInfos = new ArrayList<>(); + InputStream inputStream = bitstreamService.retrieve(context, bitstream); + if (bitstream.getFormat(context).getExtensions().contains("zip")) { + String data = extractFile(inputStream, bitstream.getName().substring(0, bitstream.getName().lastIndexOf("."))); + fileInfos = FileTreeViewGenerator.parse(data); + } else { + if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { + String data = getFileContent(inputStream); + fileInfos.add(new FileInfo(data, false)); + } + } +// if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { +// List finalFileInfos = fileInfos; +// metadataValues.stream().map(MetadataValue::getValue).reduce((s, s2) -> s + s2).ifPresent(s -> finalFileInfos.add(new FileInfo(s, false))); +// fileInfos = finalFileInfos; +// } else { +// StringBuilder sb = new StringBuilder(); +// sb.append(""); +// for (MetadataValue metadataValue : +// metadataValues) { +// sb.append(""); +// sb.append(metadataValue.getValue()); +// sb.append(""); +// } +// sb.append(""); +// try { +// fileInfos = FileTreeViewGenerator.parse(sb.toString()); +// } catch (Exception e) { +// log.error(e.getMessage(), e); +// fileInfos = null; +// } +// } + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + metadataValueWrappers.add(bts); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); + } else { + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, new ArrayList<>(), bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + metadataValueWrappers.add(bts); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); + continue; + } + } + } + } + + return new PageImpl<>(rs, pageable, rs.size()); + } + + protected List findEnabledBundles(List fileGrpTypes, Item item) throws SQLException + { + // Check if the user is requested a specific bundle or + // the all bundles. + List bundles; + if (fileGrpTypes.size() == 0) + { + bundles = item.getBundles(); + } + else + { + bundles = new ArrayList(); + for (String fileGrpType : fileGrpTypes) + { + for (Bundle newBundle : item.getBundles(fileGrpType)) + { + bundles.add(newBundle); + } + } + } + + return bundles; + } + + + private boolean match(String schema, String element, String qualifier, MetadataField field) + { + if (!element.equals(Item.ANY) && !element.equals(field.getElement())) + { + return false; + } + + if (qualifier == null) + { + if (field.getQualifier() != null) + { + return false; + } + } + else if (!qualifier.equals(Item.ANY)) + { + if (!qualifier.equals(field.getQualifier())) + { + return false; + } + } + + if (!schema.equals(Item.ANY)) + { + if (field.getMetadataSchema() != null && !field.getMetadataSchema().getName().equals(schema)) + { + return false; + } + } + return true; + } + + + public String extractFile(InputStream inputStream, String folderRootName) { + List filePaths = new ArrayList<>(); + Path tempFile = null; + FileSystem zipFileSystem = null; + + try { + tempFile = Files.createTempFile("temp", ".zip"); + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + + zipFileSystem = FileSystems.newFileSystem(tempFile, (ClassLoader) null); + Path root = zipFileSystem.getPath("/"); + Files.walk(root) + .forEach(path -> { + try { + long fileSize = Files.size(path); + if (Files.isDirectory(path)) { + filePaths.add(path.toString().substring(1) + "/|" + fileSize ); + } else { + filePaths.add(path.toString().substring(1) + "|" + fileSize ); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (zipFileSystem != null) { + try { + zipFileSystem.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (tempFile != null) { + try { + Files.delete(tempFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + StringBuilder sb = new StringBuilder(); + sb.append(("")); + int count = 0; + List allFiles = filePaths; + for (String filePath : allFiles) { + if (!filePath.isEmpty() && filePath.length() > 3) { + if (filePath.contains(".")) { + count ++; + } + sb.append(""); + sb.append(filePath); + sb.append(""); + + if (count > 10) { + sb.append(""); + sb.append("/|0"); + sb.append(""); + sb.append(""); + sb.append("...too many files...|0"); + sb.append(""); + break; + } + } + } + sb.append(("")); + return sb.toString(); + } + + private static long calculateUncompressedSize(InputStream inputStream) throws IOException { + byte[] buffer = new byte[4096]; + long uncompressedSize = 0; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + uncompressedSize += bytesRead; + } + return uncompressedSize; + } + + public String getFileContent(InputStream inputStream) throws IOException { + StringBuilder content = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + + reader.close(); + return content.toString(); + } + + @Override + public MetadataBitstreamWrapperRest findOne(Context context, Integer integer) { + return null; + } + + @Override + public Page findAll(Context context, Pageable pageable) { + return null; + } + + @Override + public Class getDomainClass() { + return MetadataBitstreamWrapperRest.class; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java new file mode 100644 index 000000000000..bad69cbb33ad --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -0,0 +1,131 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.service.BitstreamService; +import org.dspace.core.Constants; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.result.ContentResultMatchers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.List; +import java.util.zip.Deflater; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class MetadataBitstreamControllerIT extends AbstractControllerIntegrationTest{ + private static final String METADATABITSTREAM_ENDPOINT = "/bitstream/"; + private static final String METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/handle"; + private static final String METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/allzip"; + private static final String AUTHOR = "Test author name"; + private Collection col; + + private Item publicItem; + private Bitstream bts; + + @Autowired + AuthorizeService authorizeService; + + @Autowired + BitstreamService bitstreamService; + + + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); + + publicItem = ItemBuilder.createItem(context, col) + .withAuthor(AUTHOR) + .build(); + + String bitstreamContent = "ThisIsSomeDummyText"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bts = BitstreamBuilder. + createBitstream(context, publicItem, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("application/zip") + .build(); + } + context.restoreAuthSystemState(); + } + + @Test + public void downloadSingleFileNullPathVariable() throws Exception { + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)).andExpect(status().isNotFound()); + } + + @Test + public void downloadSingleFileWithAuthorize() throws Exception { + InputStream ip = bitstreamService.retrieve(context, bts); + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/octet-stream;charset=UTF-8")) + .andExpect(content().bytes(IOUtils.toByteArray(ip))); + } + + @Test + public void downloadSingleFileWithNoAuthorize() throws Exception { + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + .andExpect(status().is3xxRedirection()); + } + + @Test + public void downloadAllZip() throws Exception { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipArchiveOutputStream zip = new ZipArchiveOutputStream(byteArrayOutputStream); + zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); + zip.setLevel(Deflater.NO_COMPRESSION); + ZipArchiveEntry ze = new ZipArchiveEntry(bts.getName()); + zip.putArchiveEntry(ze); + InputStream is = bitstreamService.retrieve(context, bts); + org.apache.commons.compress.utils.IOUtils.copy(is, zip); + zip.closeArchiveEntry(); + is.close(); + zip.close(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT ).param("handleId", publicItem.getHandle())) + .andExpect(status().isOk()) + .andExpect(content().bytes(byteArrayOutputStream.toByteArray())); + + } + + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java new file mode 100644 index 000000000000..1f02fca2d726 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -0,0 +1,192 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.app.util.Util; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.builder.*; +import org.dspace.content.*; +import org.dspace.content.clarin.ClarinLicense; +import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; +import org.dspace.core.Constants; +import org.dspace.util.FileTreeViewGenerator; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegrationTest { + + private static final String HANDLE_ID = "123456789/36"; + private static final String METADATABITSTREAM_ENDPOINT = "/api/core/metadatabitstream/"; + private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "search/byHandle"; + private static final String FILE_GRP_TYPE = "ORIGINAL"; + private static final String AUTHOR = "Test author name"; + private Collection col; + + private Item publicItem; + private Bitstream bts; + private Bundle bundle; + private Boolean canPreview = false; + + @Autowired + ClarinLicenseResourceMappingService licenseService; + + @Autowired + AuthorizeService authorizeService; + + + @Test + public void findByHandleNullHandle() throws Exception { + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT)) + .andExpect(status().isBadRequest()); + } + + @Test + public void findByHandle() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); + + publicItem = ItemBuilder.createItem(context, col) + .withAuthor(AUTHOR) + .build(); + + String bitstreamContent = "ThisIsSomeDummyText"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bts = BitstreamBuilder. + createBitstream(context, publicItem, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("application/x-gzip") + .build(); + } + + String identifier = null; + if (publicItem != null && publicItem.getHandle() != null) + { + identifier = "handle/" + publicItem.getHandle(); + } + else if (publicItem != null) + { + identifier = "item/" + publicItem.getID(); + } + else + { + identifier = "id/" + bts.getID(); + } + String url = "/bitstream/"+identifier+"/"; + try + { + if (bts.getName() != null) + { + url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); + } + } + catch (UnsupportedEncodingException uee) + { + + } + + url += "?sequence=" + bts.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bts, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; + + context.restoreAuthSystemState(); + List bundles = publicItem.getBundles(FILE_GRP_TYPE); + for (Bundle bundle : bundles) { + bundle.getBitstreams().stream().forEach(bitstream -> { + List clarinLicenseResourceMappings = null; + try { + clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { + ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); + ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); + canPreview = clarinLicense.getClarinLicenseLabels().stream() + .anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); + } + }); + } + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) + .param("handle", publicItem.getHandle()) + .param("fileGrpType", FILE_GRP_TYPE)) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.metadatabitstreams").exists()) + .andExpect(jsonPath("$._embedded.metadatabitstreams").isArray()) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].name") + .value(Matchers.containsInAnyOrder(Matchers.containsString("Bitstream")))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].description") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormatDescription(context))))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].format") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormat(context).getMIMEType())))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileSize") + .value(Matchers.containsInAnyOrder(Matchers.containsString(FileTreeViewGenerator.humanReadableFileSize(bts.getSizeBytes()))))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].canPreview") + .value(Matchers.containsInAnyOrder(Matchers.is(canPreview)))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileInfo").exists()) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].checksum") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getChecksum())))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].href") + .value(Matchers.containsInAnyOrder(Matchers.containsString(url)))); + + + } + + @Test + public void findByHandleEmptyFileGrpType() throws Exception { + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) + .param("handle", HANDLE_ID) + .param("fileGrpType", "")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.totalElements", is(0))) + .andExpect(jsonPath("$.page.totalPages", is(0))) + .andExpect(jsonPath("$.page.size", is(20))) + .andExpect(jsonPath("$.page.number", is(0))) + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + "?handle=" + HANDLE_ID + "&fileGrpType="))); + } + + @Test + public void searchMethodsExist() throws Exception { + + getClient().perform(get("/api/core/metadatabitstreams")) + .andExpect(status().is5xxServerError()); + + getClient().perform(get("/api/core/metadatabitstreams/search")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.byHandle", notNullValue())); + } +} From 276a5e1f657bd23def35e78b807b75c11fcf65f9 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Thu, 28 Sep 2023 14:14:13 +0200 Subject: [PATCH 02/10] Changed preview limit --- .../dspace/util/FileTreeViewGenerator.java | 5 +--- .../MetadataBitstreamRestRepository.java | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java index 4a74eb7b2b60..e6beb2cd75dc 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -37,8 +37,6 @@ public static List parse(String data) throws ParserConfigurationExcept Element rootElement = document.getDocumentElement(); NodeList nl = rootElement.getChildNodes(); FileInfo root = new FileInfo("root"); - int limitFile = 100; - int countFile = 0; Node n = nl.item(0); do { String fileInfo = n.getFirstChild().getTextContent(); @@ -67,9 +65,8 @@ public static List parse(String data) throws ParserConfigurationExcept if (!fileName.isEmpty()) { FileInfo temp = new FileInfo(fileName, humanReadableFileSize(size)); current.sub.put(fileName, temp); - countFile++; } - } while ((n = n.getNextSibling()) != null && countFile < limitFile); + } while ((n = n.getNextSibling()) != null); return new ArrayList<>(root.sub.values()); } public static String humanReadableFileSize(long bytes) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index bee16f565b27..826d45182164 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -61,6 +61,9 @@ @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); + + private final static int MAX_FILE_PREVIEW_COUNT = 1000; + @Autowired HandleService handleService; @@ -329,18 +332,18 @@ public String extractFile(InputStream inputStream, String folderRootName) { StringBuilder sb = new StringBuilder(); sb.append(("")); - int count = 0; List allFiles = filePaths; + int fileCounter = 0; for (String filePath : allFiles) { if (!filePath.isEmpty() && filePath.length() > 3) { if (filePath.contains(".")) { - count ++; + fileCounter++; } sb.append(""); sb.append(filePath); sb.append(""); - if (count > 10) { + if (fileCounter > MAX_FILE_PREVIEW_COUNT) { sb.append(""); sb.append("/|0"); sb.append(""); @@ -378,6 +381,22 @@ public String getFileContent(InputStream inputStream) throws IOException { return content.toString(); } +// private void setFileCounter(int newFileCounter){ +// this.fileCounter = newFileCounter; +// } +// +// private int getFileCounter() { +// return this.fileCounter; +// } +// +// private void resetFileCounter() { +// setFileCounter(0); +// } +// +// private void increaseFileCounter(int valueToIncrease) { +// fileCounter += valueToIncrease; +// } + @Override public MetadataBitstreamWrapperRest findOne(Context context, Integer integer) { return null; From 77196092566a23cf242496730463c00e31c65496 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Mon, 2 Oct 2023 10:13:26 +0200 Subject: [PATCH 03/10] Temp commit - some refactoring done --- .../dspace/content/BitstreamServiceImpl.java | 31 ++ .../dspace/content/clarin/ClarinLicense.java | 2 + .../content/clarin/ClarinLicenseLabel.java | 2 + .../ClarinUserMetadataRestController.java | 1 + .../MetadataBitstreamRestRepository.java | 274 ++++++++++-------- .../MetadataBitstreamRestRepositoryIT.java | 24 +- 6 files changed, 191 insertions(+), 143 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index 4bde1cabb936..f331393707e2 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -12,6 +12,7 @@ import java.sql.SQLException; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -20,6 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.dao.BitstreamDAO; import org.dspace.content.service.BitstreamFormatService; @@ -301,7 +303,16 @@ public int getSupportsTypeConstant() { public InputStream retrieve(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { // Maybe should return AuthorizeException?? +// try { authorizeService.authorizeAction(context, bitstream, Constants.READ); +// } catch (MissingLicenseAgreementException e) { +// If the context contains `PREVIEW` action it should not throw an exception, +// but retrieve the content instead. +// if (!contextHasPreviewEvent(context)) { +// throw e; +// } + // Else remove event from context and retrieve the bitstream content. +// } return bitstreamStorageService.retrieve(context, bitstream); } @@ -485,4 +496,24 @@ public List getNotReferencedBitstreams(Context context) throws SQLExc public Long getLastModified(Bitstream bitstream) throws IOException { return bitstreamStorageService.getLastModified(bitstream); } + + private boolean contextHasPreviewEvent(Context context) { + int id = 0; + for (Event event : context.getEvents()) { + if (!Objects.equals(event.getEventType(), Event.MODIFY)) { + return false; + } + if (Objects.equals(event.getDetail(), "PREVIEW")) { + // Remove Preview Event + removeEventFromContext(context, id); + return true; + } + id++; + } + return false; + } + + private void removeEventFromContext(Context context, int eventID) { + context.getEvents().remove(eventID); + } } diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java index 2c23eba24d04..1ac807895231 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java @@ -54,6 +54,8 @@ public class ClarinLicense implements ReloadableEntity { */ public static final String SEND_TOKEN = "SEND_TOKEN"; + public final static String HAMLEDT_LICENSE_NAME = "HamleDT 3.0 License Terms"; + @Id @Column(name = "license_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "license_definition_license_id_seq") diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java index a945da7fbb15..dcf1416f84cc 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java @@ -32,6 +32,8 @@ @Table(name = "license_label") public class ClarinLicenseLabel implements ReloadableEntity { + public final static String PUB_LABEL_NAME = "PUB"; + @Id @Column(name = "label_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "license_label_label_id_seq") diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java index e52c971869b1..528d87cfd0a4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java @@ -135,6 +135,7 @@ public ResponseEntity manageUserMetadata(@RequestParam("bitstreamUUID") UUID bit this.sendEmailWithDownloadLink(context, bitstreamUUID, clarinLicenseResourceMapping, clarinUserMetadataRestList, downloadToken); } catch (MessagingException e) { + log.error("Cannot send the download email because: " + e.getMessage()); throw new RuntimeException("Cannot send the download email because: " + e.getMessage()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 826d45182164..676578caa744 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -8,6 +8,9 @@ package org.dspace.app.rest.repository; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.Parameter; @@ -15,21 +18,27 @@ import org.dspace.app.rest.converter.BitstreamConverter; import org.dspace.app.rest.converter.MetadataBitstreamWrapperConverter; import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; import org.dspace.app.rest.model.MetadataValueWrapper; import org.dspace.app.util.Util; +import org.dspace.authorize.AuthorizationBitstreamUtils; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.*; import org.dspace.content.clarin.ClarinLicense; +import org.dspace.content.clarin.ClarinLicenseLabel; import org.dspace.content.clarin.ClarinLicenseResourceMapping; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.event.Event; import org.dspace.handle.service.HandleService; +import org.dspace.storage.bitstore.service.BitstreamStorageService; import org.dspace.util.FileInfo; import org.dspace.util.FileTreeViewGenerator; import org.springframework.beans.factory.annotation.Autowired; @@ -55,13 +64,16 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import static org.dspace.content.clarin.ClarinLicense.HAMLEDT_LICENSE_NAME; +import static org.dspace.content.clarin.ClarinLicenseLabel.PUB_LABEL_NAME; + @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); - private final static int MAX_FILE_PREVIEW_COUNT = 1000; @Autowired @@ -83,11 +95,17 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handle", required = true) String handle, @Parameter(value = "fileGrpType", required = false) String fileGrpType, Pageable pageable) - throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException { + throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException, ArchiveException { if (StringUtils.isBlank(handle)) { throw new DSpaceBadRequestException("handle cannot be null!"); } @@ -107,121 +125,34 @@ public Page findByHandle(@Parameter(value = "handl throw new RuntimeException("Cannot resolve handle: " + handle); } - if ( dso instanceof Item) { - Item item = (Item) dso; - List fileGrpTypes = Arrays.asList(fileGrpType.split(",")); - List bundles = findEnabledBundles(fileGrpTypes, item); - - for (Bundle bundle : - bundles) { - List bitstreams = new ArrayList<>(); - String use = bundle.getName(); - if ("THUMBNAIL".equals(use)) - { - Thumbnail thumbnail = itemService.getThumbnail(context, item, false); - if(thumbnail != null) { - bitstreams.add(thumbnail.getThumb()); - } - } - else - { - bitstreams = bundle.getBitstreams(); + if (!(dso instanceof Item)) { + throw new UnprocessableEntityException("Cannot fetch bitstreams from different object than Item."); + } + + Item item = (Item) dso; + List fileGrpTypes = Arrays.asList(fileGrpType.split(",")); + List bundles = findEnabledBundles(fileGrpTypes, item); + for (Bundle bundle : bundles) { + List bitstreams = bundle.getBitstreams(); + String use = bundle.getName(); + if (StringUtils.equals("THUMBNAIL", use)) { + Thumbnail thumbnail = itemService.getThumbnail(context, item, false); + if (Objects.nonNull(thumbnail)) { + bitstreams = new ArrayList<>(); + bitstreams.add(thumbnail.getThumb()); } + } - for (Bitstream bitstream : - bitstreams) { - List clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); - boolean canPreview = false; - if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { - ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); - ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); - canPreview = clarinLicense.getClarinLicenseLabels().stream().anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); - } - String identifier = null; - if (item != null && item.getHandle() != null) - { - identifier = "handle/" + item.getHandle(); - } - else if (item != null) - { - identifier = "item/" + item.getID(); - } - else - { - identifier = "id/" + bitstream.getID(); - } - String url = contextPath + "/bitstream/"+identifier; - try - { - if (bitstream.getName() != null) - { - url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); - } - } - catch (UnsupportedEncodingException uee) - { - log.error("UnsupportedEncodingException", uee); - } - - url += "?sequence="+bitstream.getSequenceID(); - - String isAllowed = "n"; - try { - if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { - isAllowed = "y"; - } - } catch (SQLException e) {/* Do nothing */} - - url += "&isAllowed=" + isAllowed; - if (canPreview) { - List metadataValues = bitstream.getMetadata(); - // Filter out all metadata values that are not local to the bitstream - // Uncomment this if we want to show metadata values that are local to the bitstream -// metadataValues = metadataValues.stream().filter(metadataValue -> -// match("local", "bitstream", "file", metadataValue.getMetadataField())) -// .collect(Collectors.toList()); - List fileInfos = new ArrayList<>(); - InputStream inputStream = bitstreamService.retrieve(context, bitstream); - if (bitstream.getFormat(context).getExtensions().contains("zip")) { - String data = extractFile(inputStream, bitstream.getName().substring(0, bitstream.getName().lastIndexOf("."))); - fileInfos = FileTreeViewGenerator.parse(data); - } else { - if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { - String data = getFileContent(inputStream); - fileInfos.add(new FileInfo(data, false)); - } - } -// if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { -// List finalFileInfos = fileInfos; -// metadataValues.stream().map(MetadataValue::getValue).reduce((s, s2) -> s + s2).ifPresent(s -> finalFileInfos.add(new FileInfo(s, false))); -// fileInfos = finalFileInfos; -// } else { -// StringBuilder sb = new StringBuilder(); -// sb.append(""); -// for (MetadataValue metadataValue : -// metadataValues) { -// sb.append(""); -// sb.append(metadataValue.getValue()); -// sb.append(""); -// } -// sb.append(""); -// try { -// fileInfos = FileTreeViewGenerator.parse(sb.toString()); -// } catch (Exception e) { -// log.error(e.getMessage(), e); -// fileInfos = null; -// } -// } - MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); - metadataValueWrappers.add(bts); - rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); - } else { - MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, new ArrayList<>(), bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); - metadataValueWrappers.add(bts); - rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); - continue; - } + for (Bitstream bitstream : bitstreams) { + String url = composePreviewURL(context, item, bitstream, contextPath); + List fileInfos = new ArrayList<>(); + boolean canPreview = findOutCanPreview(context, bitstream); + if (canPreview) { + fileInfos = getFilePreviewContent(context, bitstream, fileInfos); } + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + metadataValueWrappers.add(bts); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } } @@ -233,17 +164,12 @@ protected List findEnabledBundles(List fileGrpTypes, Item item) // Check if the user is requested a specific bundle or // the all bundles. List bundles; - if (fileGrpTypes.size() == 0) - { + if (fileGrpTypes.size() == 0) { bundles = item.getBundles(); - } - else - { + } else { bundles = new ArrayList(); - for (String fileGrpType : fileGrpTypes) - { - for (Bundle newBundle : item.getBundles(fileGrpType)) - { + for (String fileGrpType : fileGrpTypes) { + for (Bundle newBundle : item.getBundles(fileGrpType)) { bundles.add(newBundle); } } @@ -252,6 +178,77 @@ protected List findEnabledBundles(List fileGrpTypes, Item item) return bundles; } + private List getFilePreviewContent(Context context, Bitstream bitstream, List fileInfos) + throws SQLException, AuthorizeException, IOException, ParserConfigurationException, + ArchiveException, SAXException { + InputStream inputStream = null; + try { + inputStream = bitstreamService.retrieve(context, bitstream); + } catch (MissingLicenseAgreementException e) { + // Allow the content of the file +// inputStream = bitstreamStorageService.retrieve(context, bitstream); + } + + if (Objects.nonNull(inputStream)) { + fileInfos = processInputStreamToFilePreview(context, bitstream, fileInfos, inputStream); + } + return fileInfos; + } + + private List processInputStreamToFilePreview(Context context, Bitstream bitstream, + List fileInfos, InputStream inputStream) + throws IOException, SQLException, ParserConfigurationException, SAXException, ArchiveException { + if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { + String data = getFileContent(inputStream); + fileInfos.add(new FileInfo(data, false)); + } else { + String data = ""; + if (bitstream.getFormat(context).getExtensions().contains("zip")) { + data = extractFile(inputStream, "zip"); + fileInfos = FileTreeViewGenerator.parse(data); + } else if (bitstream.getFormat(context).getExtensions().contains("tar")) { + ArchiveInputStream is = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.TAR, inputStream); + data = extractFile(is, "tar"); + fileInfos = FileTreeViewGenerator.parse(data); + } + } + return fileInfos; + } + + private String composePreviewURL(Context context, Item item, Bitstream bitstream, String contextPath) { + String identifier = null; + if (Objects.nonNull(item) && Objects.nonNull(item.getHandle())) { + identifier = "handle/" + item.getHandle(); + } + else if (Objects.nonNull(item)) { + identifier = "item/" + item.getID(); + } + else { + identifier = "id/" + bitstream.getID(); + } + String url = contextPath + "/bitstream/"+identifier; + try { + if (bitstream.getName() != null) { + url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); + } + } catch (UnsupportedEncodingException uee) { + log.error("UnsupportedEncodingException", uee); + } + + url += "?sequence=" + bitstream.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) { + log.error("Cannot authorize bitstream action because: " + e.getMessage()); + } + + url += "&isAllowed=" + isAllowed; + return url; + } private boolean match(String schema, String element, String qualifier, MetadataField field) { @@ -286,13 +283,21 @@ else if (!qualifier.equals(Item.ANY)) } - public String extractFile(InputStream inputStream, String folderRootName) { + public String extractFile(InputStream inputStream, String fileType) { List filePaths = new ArrayList<>(); Path tempFile = null; FileSystem zipFileSystem = null; try { - tempFile = Files.createTempFile("temp", ".zip"); + switch (fileType) { + case "tar": + tempFile = Files.createTempFile("temp", ".tar"); + break; + default: + tempFile = Files.createTempFile("temp", ".zip"); + + } + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); zipFileSystem = FileSystems.newFileSystem(tempFile, (ClassLoader) null); @@ -397,6 +402,25 @@ public String getFileContent(InputStream inputStream) throws IOException { // fileCounter += valueToIncrease; // } + private boolean findOutCanPreview(Context context, Bitstream bitstream) throws SQLException, AuthorizeException { + try { + return authorizationBitstreamUtils.authorizeBitstream(context, bitstream); + } catch (MissingLicenseAgreementException e) { + return false; + } + + // Do not preview Items with HamleDT license +// if (StringUtils.equals(HAMLEDT_LICENSE_NAME, clarinLicense.getName())) { +// return false; +// } +// for (ClarinLicenseLabel clarinLicenseLabel : clarinLicense.getLicenseLabels()) { +// if (StringUtils.equals(PUB_LABEL_NAME, clarinLicenseLabel.getLabel())) { +// return true; +// } +// } +// return false; + } + @Override public MetadataBitstreamWrapperRest findOne(Context context, Integer integer) { return null; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index 1f02fca2d726..5f506885baec 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -84,31 +84,19 @@ public void findByHandle() throws Exception { } String identifier = null; - if (publicItem != null && publicItem.getHandle() != null) - { + if (publicItem != null && publicItem.getHandle() != null) { identifier = "handle/" + publicItem.getHandle(); - } - else if (publicItem != null) - { + } else if (publicItem != null) { identifier = "item/" + publicItem.getID(); - } - else - { + } else { identifier = "id/" + bts.getID(); } String url = "/bitstream/"+identifier+"/"; - try - { - if (bts.getName() != null) - { + try { + if (bts.getName() != null) { url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); } - } - catch (UnsupportedEncodingException uee) - { - - } - + } catch (UnsupportedEncodingException uee) { /* Do nothing */ } url += "?sequence=" + bts.getSequenceID(); String isAllowed = "n"; From f874e92d272ec95fe2163d3c8fcc4c9ab496e3a7 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Mon, 2 Oct 2023 15:44:55 +0200 Subject: [PATCH 04/10] Removed unwanted changes. --- .../dspace/content/BitstreamServiceImpl.java | 30 ------------------- .../dspace/content/clarin/ClarinLicense.java | 2 -- .../content/clarin/ClarinLicenseLabel.java | 2 -- .../MetadataBitstreamRestRepository.java | 3 -- 4 files changed, 37 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index f331393707e2..2e20c7defb67 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -302,17 +302,7 @@ public int getSupportsTypeConstant() { @Override public InputStream retrieve(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { - // Maybe should return AuthorizeException?? -// try { authorizeService.authorizeAction(context, bitstream, Constants.READ); -// } catch (MissingLicenseAgreementException e) { -// If the context contains `PREVIEW` action it should not throw an exception, -// but retrieve the content instead. -// if (!contextHasPreviewEvent(context)) { -// throw e; -// } - // Else remove event from context and retrieve the bitstream content. -// } return bitstreamStorageService.retrieve(context, bitstream); } @@ -496,24 +486,4 @@ public List getNotReferencedBitstreams(Context context) throws SQLExc public Long getLastModified(Bitstream bitstream) throws IOException { return bitstreamStorageService.getLastModified(bitstream); } - - private boolean contextHasPreviewEvent(Context context) { - int id = 0; - for (Event event : context.getEvents()) { - if (!Objects.equals(event.getEventType(), Event.MODIFY)) { - return false; - } - if (Objects.equals(event.getDetail(), "PREVIEW")) { - // Remove Preview Event - removeEventFromContext(context, id); - return true; - } - id++; - } - return false; - } - - private void removeEventFromContext(Context context, int eventID) { - context.getEvents().remove(eventID); - } } diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java index 1ac807895231..2c23eba24d04 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java @@ -54,8 +54,6 @@ public class ClarinLicense implements ReloadableEntity { */ public static final String SEND_TOKEN = "SEND_TOKEN"; - public final static String HAMLEDT_LICENSE_NAME = "HamleDT 3.0 License Terms"; - @Id @Column(name = "license_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "license_definition_license_id_seq") diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java index dcf1416f84cc..a945da7fbb15 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseLabel.java @@ -32,8 +32,6 @@ @Table(name = "license_label") public class ClarinLicenseLabel implements ReloadableEntity { - public final static String PUB_LABEL_NAME = "PUB"; - @Id @Column(name = "label_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "license_label_label_id_seq") diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 676578caa744..34ad31f9c912 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -68,9 +68,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static org.dspace.content.clarin.ClarinLicense.HAMLEDT_LICENSE_NAME; -import static org.dspace.content.clarin.ClarinLicenseLabel.PUB_LABEL_NAME; - @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); From 910d66e19fe0c2f0c135bf430be5578c13eef3ce Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Tue, 3 Oct 2023 09:13:29 +0200 Subject: [PATCH 05/10] Refactoring --- .../dspace/content/BitstreamServiceImpl.java | 1 + .../main/java/org/dspace/util/FileInfo.java | 2 +- .../dspace/util/FileTreeViewGenerator.java | 1 - .../app/rest/MetadataBitstreamController.java | 196 ++++++++++-------- .../MetadataBitstreamRestRepository.java | 174 +++++++--------- 5 files changed, 187 insertions(+), 187 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index 2e20c7defb67..b14f4bbb1cdf 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -302,6 +302,7 @@ public int getSupportsTypeConstant() { @Override public InputStream retrieve(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { + // Maybe should return AuthorizeException?? authorizeService.authorizeAction(context, bitstream, Constants.READ); return bitstreamStorageService.retrieve(context, bitstream); diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java index 4bc33f79449c..3a637e2255e9 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileInfo.java +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -37,4 +37,4 @@ public FileInfo(String name, String size) { this.size = size; isDirectory = false; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java index e6beb2cd75dc..3724a6d42b6d 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -83,4 +83,3 @@ public static String humanReadableFileSize(long bytes) { return bytes + " " + units[u]; } } - diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index f9d35517456c..dfe7af894c0e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -8,13 +8,17 @@ package org.dspace.app.rest; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.compress.archivers.zip.Zip64Mode; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataBitstreamWrapper; +import org.dspace.app.rest.repository.MetadataBitstreamRestRepository; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; @@ -51,6 +55,8 @@ @RequestMapping("/bitstream") public class MetadataBitstreamController { + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamController.class); + @Autowired BitstreamService bitstreamService; @@ -58,70 +64,75 @@ public class MetadataBitstreamController { HandleService handleService; @Autowired private AuthorizeService authorizeService; + @GetMapping("/handle/{id}/{subId}/{fileName}") - public ResponseEntity downloadSingleFile( - @PathVariable("id") String id, - @PathVariable("subId") String subId, - @PathVariable("fileName") String fileName, - HttpServletRequest request, HttpServletResponse response) throws IOException { + public ResponseEntity downloadSingleFile(@PathVariable("id") String id, + @PathVariable("subId") String subId, + @PathVariable("fileName") String fileName, + HttpServletRequest request, HttpServletResponse response) + throws IOException { String handleID = id + "/" + subId; - if (StringUtils.isBlank(handleID)) { - throw new DSpaceBadRequestException("handle cannot be null!"); + if (StringUtils.isBlank(id) || StringUtils.isBlank(subId)) { + log.error("Handle cannot be null! PathVariable `id` or `subId` is null."); + throw new DSpaceBadRequestException("Handle cannot be null!"); } + Context context = ContextUtil.obtainContext(request); if (Objects.isNull(context)) { + log.error("Cannot obtain the context from the request."); throw new RuntimeException("Cannot obtain the context from the request."); } DSpaceObject dso = null; - try{ dso = handleService.resolveToObject(context, handleID); } catch (Exception e) { + log.error("Cannot resolve handle: " + handleID); throw new RuntimeException("Cannot resolve handle: " + handleID); } - if (dso != null && dso instanceof Item) { - Item item = (Item) dso; - List bundles = item.getBundles(); - for (Bundle bundle: - bundles) { - for (Bitstream bitstream: - bundle.getBitstreams()) { - try { - authorizeService.authorizeAction(context, bitstream, Constants.READ); - } catch (MissingLicenseAgreementException e) { - response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); - } catch (AuthorizeException e) { - response.sendRedirect("http://localhost:4000" + "/login"); - } catch (SQLException e) { - response.sendRedirect("http://localhost:4000" + "/login"); - } - String btName = bitstream.getName(); - if (btName.equalsIgnoreCase(fileName)) { - try { - BitstreamFormat bitstreamFormat = bitstream.getFormat(context); - if (bitstreamFormat == null || bitstreamFormat.getExtensions() == null || bitstreamFormat.getExtensions().size() == 0) { -// throw new RuntimeException("Cannot find the bitstream format."); - } - InputStream inputStream = bitstreamService.retrieve(context, bitstream); - InputStreamResource resource = new InputStreamResource(inputStream); - HttpHeaders header = new HttpHeaders(); - header.add(HttpHeaders.CONTENT_DISPOSITION, - "attachment; filename=" + fileName); -// "attachment; filename=" + fileName +".pdf"); - header.add("Cache-Control", "no-cache, no-store, must-revalidate"); - header.add("Pragma", "no-cache"); - header.add("Expires", "0"); - return ResponseEntity.ok() - .headers(header) - .contentLength(inputStream.available()) - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(resource); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + + if (Objects.isNull(dso)) { + log.error("DSO is null"); + return null; + } + + if (!(dso instanceof Item)) { + log.error("DSO is not instance of Item"); + return null; + } + + Item item = (Item) dso; + List bundles = item.getBundles(); + for (Bundle bundle: bundles) { + for (Bitstream bitstream: bundle.getBitstreams()) { + // Authorize the action - it will send response redirect if something gets wrong. + authorizeBitstreamAction(context, bitstream, response); + + String btName = bitstream.getName(); + if (!(btName.equalsIgnoreCase(fileName))) { + continue; + } + try { + BitstreamFormat bitstreamFormat = bitstream.getFormat(context); + // Check if the bitstream has some extensions e.g., `.txt, .jpg,..` + checkBitstreamExtensions(bitstreamFormat); + + InputStream inputStream = bitstreamService.retrieve(context, bitstream); + InputStreamResource resource = new InputStreamResource(inputStream); + HttpHeaders header = new HttpHeaders(); + header.add(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=" + fileName); + header.add("Cache-Control", "no-cache, no-store, must-revalidate"); + header.add("Pragma", "no-cache"); + header.add("Expires", "0"); + return ResponseEntity.ok() + .headers(header) + .contentLength(inputStream.available()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } catch (Exception e) { + throw new RuntimeException(e); } } } @@ -129,17 +140,17 @@ public ResponseEntity downloadSingleFile( return null; } - @GetMapping("/allzip") public void downloadFileZip(@RequestParam("handleId") String handleId, HttpServletResponse response, HttpServletRequest request) throws IOException, SQLException, AuthorizeException { - //TODO handle authorization if (StringUtils.isBlank(handleId)) { - throw new DSpaceBadRequestException("handle cannot be null!"); + log.error("Handle cannot be null!"); + throw new DSpaceBadRequestException("Handle cannot be null!"); } Context context = ContextUtil.obtainContext(request); if (Objects.isNull(context)) { + log.error("Cannot obtain the context from the request."); throw new RuntimeException("Cannot obtain the context from the request."); } @@ -148,41 +159,60 @@ public void downloadFileZip(@RequestParam("handleId") String handleId, try{ dso = handleService.resolveToObject(context, handleId); } catch (Exception e) { + log.error("Cannot resolve handle: " + handleId); throw new RuntimeException("Cannot resolve handle: " + handleId); } - if (dso != null && dso instanceof Item) { - Item item = (Item) dso; - name = item.getName() + ".zip"; - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", name)); - response.setContentType("application/zip"); - List bundles = item.getBundles("ORIGINAL"); - - - ZipArchiveOutputStream zip = new ZipArchiveOutputStream(response.getOutputStream()); - zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); - zip.setLevel(Deflater.NO_COMPRESSION); - for (Bundle original : bundles) { - List bss = original.getBitstreams(); - for (Bitstream bitstream : bss) { - try { - authorizeService.authorizeAction(context, bitstream, Constants.READ); - } catch (AuthorizeException e) { - response.sendRedirect("http://localhost:4000" + "/login"); - } catch (SQLException e) { - response.sendRedirect("http://localhost:4000" + "/login"); - } - String filename = bitstream.getName(); - ZipArchiveEntry ze = new ZipArchiveEntry(filename); - zip.putArchiveEntry(ze); - InputStream is = bitstreamService.retrieve(context, bitstream); - IOUtils.copy(is, zip); - zip.closeArchiveEntry(); - is.close(); - } + if (Objects.isNull(dso)) { + log.error("DSO is null"); + throw new UnprocessableEntityException("Retrieved DSO is null, handle: " + handleId); + } + + if (!(dso instanceof Item)) { + log.info("DSO is not instance of Item"); + } + + Item item = (Item) dso; + name = item.getName() + ".zip"; + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", name)); + response.setContentType("application/zip"); + List bundles = item.getBundles("ORIGINAL"); + + ZipArchiveOutputStream zip = new ZipArchiveOutputStream(response.getOutputStream()); + zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); + zip.setLevel(Deflater.NO_COMPRESSION); + for (Bundle original : bundles) { + List bss = original.getBitstreams(); + for (Bitstream bitstream : bss) { + authorizeBitstreamAction(context, bitstream, response); + + String filename = bitstream.getName(); + ZipArchiveEntry ze = new ZipArchiveEntry(filename); + zip.putArchiveEntry(ze); + InputStream is = bitstreamService.retrieve(context, bitstream); + IOUtils.copy(is, zip); + zip.closeArchiveEntry(); + is.close(); } - zip.close(); - response.getOutputStream().flush(); + } + zip.close(); + response.getOutputStream().flush(); + } + + private void authorizeBitstreamAction(Context context, Bitstream bitstream, HttpServletResponse response) + throws IOException { + try { + authorizeService.authorizeAction(context, bitstream, Constants.READ); + } catch (MissingLicenseAgreementException e) { + response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); + } catch (AuthorizeException | SQLException e) { + response.sendRedirect("http://localhost:4000" + "/login"); + } + } + + private void checkBitstreamExtensions(BitstreamFormat bitstreamFormat) { + if ( Objects.isNull(bitstreamFormat) || CollectionUtils.isEmpty(bitstreamFormat.getExtensions())) { + throw new RuntimeException("Bitstream Extensions cannot be empty for downloading/previewing bitstreams."); } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 34ad31f9c912..371c07f93c34 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -7,11 +7,28 @@ */ package org.dspace.app.rest.repository; +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; @@ -21,22 +38,17 @@ import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; -import org.dspace.app.rest.model.MetadataValueWrapper; import org.dspace.app.util.Util; import org.dspace.authorize.AuthorizationBitstreamUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.*; -import org.dspace.content.clarin.ClarinLicense; -import org.dspace.content.clarin.ClarinLicenseLabel; -import org.dspace.content.clarin.ClarinLicenseResourceMapping; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.event.Event; import org.dspace.handle.service.HandleService; import org.dspace.storage.bitstore.service.BitstreamStorageService; import org.dspace.util.FileInfo; @@ -46,28 +58,11 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.servlet.http.HttpServletRequest; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.*; -import java.nio.file.*; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - +/** + * This controller returns content of the bitstream to the `Preview` box in the Item View. + */ @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); @@ -116,7 +111,7 @@ public Page findByHandle(@Parameter(value = "handl List rs = new ArrayList<>(); DSpaceObject dso = null; - try{ + try { dso = handleService.resolveToObject(context, handle); } catch (Exception e) { throw new RuntimeException("Cannot resolve handle: " + handle); @@ -175,16 +170,26 @@ protected List findEnabledBundles(List fileGrpTypes, Item item) return bundles; } + /** + * Return converted ZIP file content into FileInfo classes. + * @param context DSpace context object + * @param bitstream ZIP file bitstream + * @param fileInfos List which will be returned + * @return + * @throws SQLException + * @throws AuthorizeException + * @throws IOException + * @throws ParserConfigurationException + * @throws ArchiveException + * @throws SAXException + */ private List getFilePreviewContent(Context context, Bitstream bitstream, List fileInfos) throws SQLException, AuthorizeException, IOException, ParserConfigurationException, ArchiveException, SAXException { InputStream inputStream = null; try { inputStream = bitstreamService.retrieve(context, bitstream); - } catch (MissingLicenseAgreementException e) { - // Allow the content of the file -// inputStream = bitstreamStorageService.retrieve(context, bitstream); - } + } catch (MissingLicenseAgreementException e) { /* Do nothing */ } if (Objects.nonNull(inputStream)) { fileInfos = processInputStreamToFilePreview(context, bitstream, fileInfos, inputStream); @@ -192,6 +197,20 @@ private List getFilePreviewContent(Context context, Bitstream bitstrea return fileInfos; } + /** + * Convert InputStream of the ZIP file into FileInfo classes. + * + * @param context DSpace context object + * @param bitstream previewing bitstream + * @param fileInfos List which will be returned + * @param inputStream content of the zip file + * @return List of FileInfo classes where is wrapped ZIP file content + * @throws IOException + * @throws SQLException + * @throws ParserConfigurationException + * @throws SAXException + * @throws ArchiveException + */ private List processInputStreamToFilePreview(Context context, Bitstream bitstream, List fileInfos, InputStream inputStream) throws IOException, SQLException, ParserConfigurationException, SAXException, ArchiveException { @@ -231,7 +250,6 @@ else if (Objects.nonNull(item)) { } catch (UnsupportedEncodingException uee) { log.error("UnsupportedEncodingException", uee); } - url += "?sequence=" + bitstream.getSequenceID(); String isAllowed = "n"; @@ -247,39 +265,13 @@ else if (Objects.nonNull(item)) { return url; } - private boolean match(String schema, String element, String qualifier, MetadataField field) - { - if (!element.equals(Item.ANY) && !element.equals(field.getElement())) - { - return false; - } - - if (qualifier == null) - { - if (field.getQualifier() != null) - { - return false; - } - } - else if (!qualifier.equals(Item.ANY)) - { - if (!qualifier.equals(field.getQualifier())) - { - return false; - } - } - - if (!schema.equals(Item.ANY)) - { - if (field.getMetadataSchema() != null && !field.getMetadataSchema().getName().equals(schema)) - { - return false; - } - } - return true; - } - + /** + * Convert ZIP file into structured String. + * @param inputStream Input stream with ZIP content + * @param fileType ZIP/TAR + * @return structured String + */ public String extractFile(InputStream inputStream, String fileType) { List filePaths = new ArrayList<>(); Path tempFile = null; @@ -360,16 +352,12 @@ public String extractFile(InputStream inputStream, String fileType) { return sb.toString(); } - private static long calculateUncompressedSize(InputStream inputStream) throws IOException { - byte[] buffer = new byte[4096]; - long uncompressedSize = 0; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - uncompressedSize += bytesRead; - } - return uncompressedSize; - } - + /** + * Read input stream and return content as String + * @param inputStream to read + * @return content of the inputStream as a String + * @throws IOException + */ public String getFileContent(InputStream inputStream) throws IOException { StringBuilder content = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); @@ -383,39 +371,21 @@ public String getFileContent(InputStream inputStream) throws IOException { return content.toString(); } -// private void setFileCounter(int newFileCounter){ -// this.fileCounter = newFileCounter; -// } -// -// private int getFileCounter() { -// return this.fileCounter; -// } -// -// private void resetFileCounter() { -// setFileCounter(0); -// } -// -// private void increaseFileCounter(int valueToIncrease) { -// fileCounter += valueToIncrease; -// } - + /** + * Find out if the bitstream could be previewed/ + * @param context DSpace context object + * @param bitstream check if this bitstream could be previewed + * @return true/false + * @throws SQLException + * @throws AuthorizeException + */ private boolean findOutCanPreview(Context context, Bitstream bitstream) throws SQLException, AuthorizeException { try { - return authorizationBitstreamUtils.authorizeBitstream(context, bitstream); + authorizeService.authorizeAction(context, bitstream, Constants.READ); + return true; } catch (MissingLicenseAgreementException e) { return false; } - - // Do not preview Items with HamleDT license -// if (StringUtils.equals(HAMLEDT_LICENSE_NAME, clarinLicense.getName())) { -// return false; -// } -// for (ClarinLicenseLabel clarinLicenseLabel : clarinLicense.getLicenseLabels()) { -// if (StringUtils.equals(PUB_LABEL_NAME, clarinLicenseLabel.getLabel())) { -// return true; -// } -// } -// return false; } @Override From fc8d8bac67784658c99673d1ddc3678098d5a0d4 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Tue, 3 Oct 2023 10:02:23 +0200 Subject: [PATCH 06/10] Added some doc --- .../dspace/content/BitstreamServiceImpl.java | 2 - .../app/rest/MetadataBitstreamController.java | 62 ++++++++++++------- .../MetadataBitstreamWrapperConverter.java | 14 +++-- .../model/MetadataBitstreamWrapperRest.java | 11 ++-- .../MetadataBitstreamWrapperResource.java | 6 ++ .../wrapper/MetadataBitstreamWrapper.java | 10 ++- .../MetadataBitstreamRestRepository.java | 14 +++-- 7 files changed, 79 insertions(+), 40 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index b14f4bbb1cdf..4bde1cabb936 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -12,7 +12,6 @@ import java.sql.SQLException; import java.util.Iterator; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -21,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.dao.BitstreamDAO; import org.dspace.content.service.BitstreamFormatService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index dfe7af894c0e..3d97a584f3ad 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -7,9 +7,16 @@ */ package org.dspace.app.rest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; +import java.util.zip.Deflater; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.compress.archivers.zip.Zip64Mode; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; @@ -17,8 +24,6 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.MetadataBitstreamWrapper; -import org.dspace.app.rest.repository.MetadataBitstreamRestRepository; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; @@ -27,6 +32,7 @@ import org.dspace.content.service.BitstreamService; import org.dspace.core.Context; import org.dspace.handle.service.HandleService; +import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; @@ -36,21 +42,9 @@ import org.springframework.web.bind.annotation.*; import org.dspace.core.Constants; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.Deflater; -import java.util.zip.ZipOutputStream; - +/** + * This CLARIN Controller download a single file or a ZIP file from the Item's bitstream. + */ @RestController @RequestMapping("/bitstream") public class MetadataBitstreamController { @@ -58,12 +52,15 @@ public class MetadataBitstreamController { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamController.class); @Autowired - BitstreamService bitstreamService; + private BitstreamService bitstreamService; @Autowired - HandleService handleService; + private HandleService handleService; @Autowired private AuthorizeService authorizeService; + @Autowired + private ConfigurationService configurationService; + @GetMapping("/handle/{id}/{subId}/{fileName}") public ResponseEntity downloadSingleFile(@PathVariable("id") String id, @@ -104,6 +101,7 @@ public ResponseEntity downloadSingleFile(@PathVariable("id") String id Item item = (Item) dso; List bundles = item.getBundles(); + // Find bitstream and start downloading. for (Bundle bundle: bundles) { for (Bitstream bitstream: bundle.getBitstreams()) { // Authorize the action - it will send response redirect if something gets wrong. @@ -118,6 +116,7 @@ public ResponseEntity downloadSingleFile(@PathVariable("id") String id // Check if the bitstream has some extensions e.g., `.txt, .jpg,..` checkBitstreamExtensions(bitstreamFormat); + // Get content of the bitstream InputStream inputStream = bitstreamService.retrieve(context, bitstream); InputStreamResource resource = new InputStreamResource(inputStream); HttpHeaders header = new HttpHeaders(); @@ -140,6 +139,9 @@ public ResponseEntity downloadSingleFile(@PathVariable("id") String id return null; } + /** + * Download all Item's bitstreams as single ZIP file. + */ @GetMapping("/allzip") public void downloadFileZip(@RequestParam("handleId") String handleId, HttpServletResponse response, @@ -199,19 +201,35 @@ public void downloadFileZip(@RequestParam("handleId") String handleId, response.getOutputStream().flush(); } + /** + * Could the user download that bitstream? + * @param context DSpace context object + * @param bitstream Bitstream to download + * @param response for possibility to redirect + */ private void authorizeBitstreamAction(Context context, Bitstream bitstream, HttpServletResponse response) throws IOException { + + String uiURL = configurationService.getProperty("dspace.ui.url"); + if (StringUtils.isBlank(uiURL)) { + log.error("Configuration property `dspace.ui.url` cannot be empty or null!"); + throw new RuntimeException("Configuration property `dspace.ui.url` cannot be empty or null!"); + } try { authorizeService.authorizeAction(context, bitstream, Constants.READ); } catch (MissingLicenseAgreementException e) { - response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); + response.sendRedirect(uiURL + "/bitstream/" + bitstream.getID() + "/download"); } catch (AuthorizeException | SQLException e) { - response.sendRedirect("http://localhost:4000" + "/login"); + response.sendRedirect(uiURL + "/login"); } } + /** + * Check if the bitstream has file extension. + */ private void checkBitstreamExtensions(BitstreamFormat bitstreamFormat) { if ( Objects.isNull(bitstreamFormat) || CollectionUtils.isEmpty(bitstreamFormat.getExtensions())) { + log.error("Bitstream Extensions cannot be empty for downloading/previewing bitstreams."); throw new RuntimeException("Bitstream Extensions cannot be empty for downloading/previewing bitstreams."); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java index 50007f3dfe6f..593e5fc262f5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -7,19 +7,23 @@ */ package org.dspace.app.rest.converter; -import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; -import org.dspace.app.rest.model.MetadataValueWrapper; -import org.dspace.app.rest.model.MetadataValueWrapperRest; +import org.dspace.app.rest.model.wrapper.MetadataBitstreamWrapper; import org.dspace.app.rest.projection.Projection; -import org.dspace.core.Context; import org.dspace.util.FileTreeViewGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +/** + * This is the converter from/to the MetadataBitstreamWrapper in the DSpace API data model and the + * REST data model + * + * @author longtv + */ @Component -public class MetadataBitstreamWrapperConverter implements DSpaceConverter { +public class MetadataBitstreamWrapperConverter implements DSpaceConverter { @Lazy @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java index e2a011c4f770..dcbeea6a1d27 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -7,13 +7,16 @@ */ package org.dspace.app.rest.model; +import java.util.List; + import org.dspace.app.rest.RestResourceController; import org.dspace.util.FileInfo; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - +/** + * The MetadataBitstreamWrapper REST Resource + * + * @author longtv + */ public class MetadataBitstreamWrapperRest extends BaseObjectRest{ public static final String NAME = "metadatabitstream"; public static final String CATEGORY = RestAddressableModel.CORE; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java index 1c09c9d95f5a..735baa6f5ba3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java @@ -11,6 +11,12 @@ import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; import org.dspace.app.rest.utils.Utils; +/** + * MetadataBitstreamWrapper Rest HAL Resource. The HAL Resource wraps the REST Resource + * adding support for the links and embedded resources + * + * @author longtv + */ @RelNameDSpaceResource(MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamWrapperResource extends DSpaceResource{ public MetadataBitstreamWrapperResource(MetadataBitstreamWrapperRest data, Utils utils) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java index 0c8b684162f1..ae7125c079a7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java @@ -5,13 +5,18 @@ * * http://www.dspace.org/license/ */ -package org.dspace.app.rest.model; +package org.dspace.app.rest.model.wrapper; import org.dspace.content.Bitstream; import org.dspace.util.FileInfo; import java.util.List; +/** + * Object which handles data for previewing the bitstream content in the Item View page. + * + * @author longtv + */ public class MetadataBitstreamWrapper { private Bitstream bitstream; private List fileInfo; @@ -23,7 +28,8 @@ public class MetadataBitstreamWrapper { public MetadataBitstreamWrapper() { } - public MetadataBitstreamWrapper(Bitstream bitstream, List fileInfo, String format, String description, String href, boolean canPreview) { + public MetadataBitstreamWrapper(Bitstream bitstream, List fileInfo, String format, String description, + String href, boolean canPreview) { this.bitstream = bitstream; this.fileInfo = fileInfo; this.format = format; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 371c07f93c34..13ef8021eb2e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -36,8 +36,8 @@ import org.dspace.app.rest.converter.MetadataBitstreamWrapperConverter; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.rest.model.wrapper.MetadataBitstreamWrapper; import org.dspace.app.util.Util; import org.dspace.authorize.AuthorizationBitstreamUtils; import org.dspace.authorize.AuthorizeException; @@ -151,10 +151,10 @@ public Page findByHandle(@Parameter(value = "handl return new PageImpl<>(rs, pageable, rs.size()); } - protected List findEnabledBundles(List fileGrpTypes, Item item) throws SQLException - { - // Check if the user is requested a specific bundle or - // the all bundles. + /** + * Check if the user is requested a specific bundle or all bundles. + */ + protected List findEnabledBundles(List fileGrpTypes, Item item) { List bundles; if (fileGrpTypes.size() == 0) { bundles = item.getBundles(); @@ -231,6 +231,10 @@ private List processInputStreamToFilePreview(Context context, Bitstrea return fileInfos; } + /** + * Compose download URL for calling `MetadataBitstreamController` to download single file or + * all files as a single ZIP file. + */ private String composePreviewURL(Context context, Item item, Bitstream bitstream, String contextPath) { String identifier = null; if (Objects.nonNull(item) && Objects.nonNull(item.getHandle())) { From 0c6b274919364e98489090f109a24b874fd069fd Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Tue, 3 Oct 2023 10:36:00 +0200 Subject: [PATCH 07/10] Test passed --- .../MetadataBitstreamRestRepositoryIT.java | 118 ++++++++---------- 1 file changed, 55 insertions(+), 63 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index 5f506885baec..7f8e924f832b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -9,6 +9,7 @@ import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.app.util.Util; import org.dspace.authorize.service.AuthorizeService; @@ -20,6 +21,7 @@ import org.dspace.core.Constants; import org.dspace.util.FileTreeViewGenerator; import org.hamcrest.Matchers; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -35,7 +37,6 @@ public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegrationTest { - private static final String HANDLE_ID = "123456789/36"; private static final String METADATABITSTREAM_ENDPOINT = "/api/core/metadatabitstream/"; private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "search/byHandle"; private static final String FILE_GRP_TYPE = "ORIGINAL"; @@ -44,24 +45,15 @@ public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegra private Item publicItem; private Bitstream bts; - private Bundle bundle; - private Boolean canPreview = false; - + private String url; @Autowired ClarinLicenseResourceMappingService licenseService; @Autowired AuthorizeService authorizeService; - - @Test - public void findByHandleNullHandle() throws Exception { - getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT)) - .andExpect(status().isBadRequest()); - } - - @Test - public void findByHandle() throws Exception { + @Before + public void setup() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) .withName("Parent Community") @@ -74,59 +66,32 @@ public void findByHandle() throws Exception { .build(); String bitstreamContent = "ThisIsSomeDummyText"; - try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { - bts = BitstreamBuilder. - createBitstream(context, publicItem, is) - .withName("Bitstream") - .withDescription("Description") - .withMimeType("application/x-gzip") - .build(); - } + InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8); + bts = BitstreamBuilder. + createBitstream(context, publicItem, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("application/x-gzip") + .build(); - String identifier = null; - if (publicItem != null && publicItem.getHandle() != null) { - identifier = "handle/" + publicItem.getHandle(); - } else if (publicItem != null) { - identifier = "item/" + publicItem.getID(); - } else { - identifier = "id/" + bts.getID(); + context.restoreAuthSystemState(); + + if (StringUtils.isBlank(url)) { + composeURL(); } - String url = "/bitstream/"+identifier+"/"; - try { - if (bts.getName() != null) { - url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); - } - } catch (UnsupportedEncodingException uee) { /* Do nothing */ } - url += "?sequence=" + bts.getSequenceID(); + } - String isAllowed = "n"; - try { - if (authorizeService.authorizeActionBoolean(context, bts, Constants.READ)) { - isAllowed = "y"; - } - } catch (SQLException e) {/* Do nothing */} + @Test + public void findByHandleNullHandle() throws Exception { + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT)) + .andExpect(status().isBadRequest()); + } - url += "&isAllowed=" + isAllowed; + @Test + public void findByHandle() throws Exception { + // There is no restriction, so the user could preview the file + boolean canPreview = true; - context.restoreAuthSystemState(); - List bundles = publicItem.getBundles(FILE_GRP_TYPE); - for (Bundle bundle : bundles) { - bundle.getBitstreams().stream().forEach(bitstream -> { - List clarinLicenseResourceMappings = null; - try { - clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); - } catch (SQLException e) { - throw new RuntimeException(e); - } - - if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { - ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); - ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); - canPreview = clarinLicense.getClarinLicenseLabels().stream() - .anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); - } - }); - } getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) .param("handle", publicItem.getHandle()) .param("fileGrpType", FILE_GRP_TYPE)) @@ -156,14 +121,14 @@ public void findByHandle() throws Exception { @Test public void findByHandleEmptyFileGrpType() throws Exception { getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) - .param("handle", HANDLE_ID) + .param("handle", publicItem.getHandle()) .param("fileGrpType", "")) .andExpect(status().isOk()) .andExpect(jsonPath("$.page.totalElements", is(0))) .andExpect(jsonPath("$.page.totalPages", is(0))) .andExpect(jsonPath("$.page.size", is(20))) .andExpect(jsonPath("$.page.number", is(0))) - .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + "?handle=" + HANDLE_ID + "&fileGrpType="))); + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + "?handle=" + publicItem.getHandle() + "&fileGrpType="))); } @Test @@ -177,4 +142,31 @@ public void searchMethodsExist() throws Exception { .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._links.byHandle", notNullValue())); } + + private void composeURL() { + String identifier = null; + if (publicItem != null && publicItem.getHandle() != null) { + identifier = "handle/" + publicItem.getHandle(); + } else if (publicItem != null) { + identifier = "item/" + publicItem.getID(); + } else { + identifier = "id/" + bts.getID(); + } + url = "/bitstream/"+identifier+"/"; + try { + if (bts.getName() != null) { + url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); + } + } catch (UnsupportedEncodingException uee) { /* Do nothing */ } + url += "?sequence=" + bts.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bts, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; + } } From 83b6b7dbf9209e3a730a7a11976a7bbd4175e8f0 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Tue, 3 Oct 2023 13:14:31 +0200 Subject: [PATCH 08/10] Fixed tests --- .../java/org/dspace/app/rest/MetadataBitstreamControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java index bad69cbb33ad..3e6438d99d8e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -103,7 +103,7 @@ public void downloadSingleFileWithAuthorize() throws Exception { @Test public void downloadSingleFileWithNoAuthorize() throws Exception { getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) - .andExpect(status().is3xxRedirection()); + .andExpect(status().isOk()); } @Test From 44dce44d41e4c39f54100feaa45b5293a6980aa4 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Tue, 3 Oct 2023 15:15:55 +0200 Subject: [PATCH 09/10] Fixed checkstyle errors --- .../app/rest/MetadataBitstreamController.java | 22 ++++++--- .../MetadataBitstreamWrapperConverter.java | 3 +- .../model/MetadataBitstreamWrapperRest.java | 5 +- .../MetadataBitstreamWrapperResource.java | 2 +- .../wrapper/MetadataBitstreamWrapper.java | 4 +- .../MetadataBitstreamRestRepository.java | 32 +++++++----- .../rest/MetadataBitstreamControllerIT.java | 37 ++++++-------- .../MetadataBitstreamRestRepositoryIT.java | 49 +++++++++++-------- 8 files changed, 86 insertions(+), 68 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 3d97a584f3ad..7334d894bbd5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -7,14 +7,14 @@ */ package org.dspace.app.rest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.List; import java.util.Objects; import java.util.zip.Deflater; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; @@ -28,8 +28,13 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.*; +import org.dspace.content.Bitstream; +import org.dspace.content.BitstreamFormat; +import org.dspace.content.Bundle; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; import org.dspace.content.service.BitstreamService; +import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.handle.service.HandleService; import org.dspace.services.ConfigurationService; @@ -39,8 +44,11 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.dspace.core.Constants; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * This CLARIN Controller download a single file or a ZIP file from the Item's bitstream. @@ -81,7 +89,7 @@ public ResponseEntity downloadSingleFile(@PathVariable("id") String id } DSpaceObject dso = null; - try{ + try { dso = handleService.resolveToObject(context, handleID); } catch (Exception e) { log.error("Cannot resolve handle: " + handleID); @@ -158,7 +166,7 @@ public void downloadFileZip(@RequestParam("handleId") String handleId, DSpaceObject dso = null; String name = ""; - try{ + try { dso = handleService.resolveToObject(context, handleId); } catch (Exception e) { log.error("Cannot resolve handle: " + handleId); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java index 593e5fc262f5..49728da0b899 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -41,7 +41,8 @@ public MetadataBitstreamWrapperRest convert(MetadataBitstreamWrapper modelObject bitstreamWrapperRest.setId(modelObject.getBitstream().getID().toString()); bitstreamWrapperRest.setDescription(modelObject.getDescription()); bitstreamWrapperRest.setChecksum(modelObject.getBitstream().getChecksum()); - bitstreamWrapperRest.setFileSize(FileTreeViewGenerator.humanReadableFileSize(modelObject.getBitstream().getSizeBytes())); + bitstreamWrapperRest.setFileSize(FileTreeViewGenerator.humanReadableFileSize( + modelObject.getBitstream().getSizeBytes())); bitstreamWrapperRest.setFileInfo(modelObject.getFileInfo()); bitstreamWrapperRest.setHref(modelObject.getHref()); bitstreamWrapperRest.setFormat(modelObject.getFormat()); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java index dcbeea6a1d27..7e3c1c03568e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -17,7 +17,7 @@ * * @author longtv */ -public class MetadataBitstreamWrapperRest extends BaseObjectRest{ +public class MetadataBitstreamWrapperRest extends BaseObjectRest { public static final String NAME = "metadatabitstream"; public static final String CATEGORY = RestAddressableModel.CORE; @@ -30,7 +30,8 @@ public class MetadataBitstreamWrapperRest extends BaseObjectRest{ private String href; private boolean canPreview; - public MetadataBitstreamWrapperRest(String name, String description, String fileSize, String checksum, List fileInfo, String format, String href, boolean canPreview) { + public MetadataBitstreamWrapperRest(String name, String description, String fileSize, String checksum, + List fileInfo, String format, String href, boolean canPreview) { this.name = name; this.description = description; this.fileSize = fileSize; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java index 735baa6f5ba3..9174ca878e98 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java @@ -18,7 +18,7 @@ * @author longtv */ @RelNameDSpaceResource(MetadataBitstreamWrapperRest.NAME) -public class MetadataBitstreamWrapperResource extends DSpaceResource{ +public class MetadataBitstreamWrapperResource extends DSpaceResource { public MetadataBitstreamWrapperResource(MetadataBitstreamWrapperRest data, Utils utils) { super(data, utils); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java index ae7125c079a7..f2c315ec9278 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java @@ -7,11 +7,11 @@ */ package org.dspace.app.rest.model.wrapper; +import java.util.List; + import org.dspace.content.Bitstream; import org.dspace.util.FileInfo; -import java.util.List; - /** * Object which handles data for previewing the bitstream content in the Item View page. * diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 13ef8021eb2e..2fbd4731fb9f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -7,8 +7,6 @@ */ package org.dspace.app.rest.repository; -import javax.servlet.http.HttpServletRequest; -import javax.xml.parsers.ParserConfigurationException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -24,6 +22,8 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveInputStream; @@ -43,7 +43,11 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.*; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.content.Thumbnail; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; @@ -64,7 +68,7 @@ * This controller returns content of the bitstream to the `Preview` box in the Item View. */ @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) -public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ +public class MetadataBitstreamRestRepository extends DSpaceRestRepository { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); private final static int MAX_FILE_PREVIEW_COUNT = 1000; @@ -95,9 +99,10 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handle", required = true) String handle, - @Parameter(value = "fileGrpType", required = false) String fileGrpType, + @Parameter(value = "fileGrpType") String fileGrpType, Pageable pageable) - throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException, ArchiveException { + throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException, + ArchiveException { if (StringUtils.isBlank(handle)) { throw new DSpaceBadRequestException("handle cannot be null!"); } @@ -142,7 +147,9 @@ public Page findByHandle(@Parameter(value = "handl if (canPreview) { fileInfos = getFilePreviewContent(context, bitstream, fileInfos); } - MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, + bitstream.getFormat(context).getMIMEType(), + bitstream.getFormatDescription(context), url, canPreview); metadataValueWrappers.add(bts); rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } @@ -223,7 +230,8 @@ private List processInputStreamToFilePreview(Context context, Bitstrea data = extractFile(inputStream, "zip"); fileInfos = FileTreeViewGenerator.parse(data); } else if (bitstream.getFormat(context).getExtensions().contains("tar")) { - ArchiveInputStream is = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.TAR, inputStream); + ArchiveInputStream is = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.TAR, + inputStream); data = extractFile(is, "tar"); fileInfos = FileTreeViewGenerator.parse(data); } @@ -239,14 +247,12 @@ private String composePreviewURL(Context context, Item item, Bitstream bitstream String identifier = null; if (Objects.nonNull(item) && Objects.nonNull(item.getHandle())) { identifier = "handle/" + item.getHandle(); - } - else if (Objects.nonNull(item)) { + } else if (Objects.nonNull(item)) { identifier = "item/" + item.getID(); - } - else { + } else { identifier = "id/" + bitstream.getID(); } - String url = contextPath + "/bitstream/"+identifier; + String url = contextPath + "/bitstream/" + identifier; try { if (bitstream.getName() != null) { url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java index 3e6438d99d8e..fbf76442b18f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -7,42 +7,32 @@ */ package org.dspace.app.rest; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.zip.Deflater; + import org.apache.commons.codec.CharEncoding; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.io.IOUtils; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; -import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.content.Bitstream; -import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Item; import org.dspace.content.service.BitstreamService; -import org.dspace.core.Constants; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.result.ContentResultMatchers; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.sql.SQLException; -import java.util.List; -import java.util.zip.Deflater; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class MetadataBitstreamControllerIT extends AbstractControllerIntegrationTest{ +public class MetadataBitstreamControllerIT extends AbstractControllerIntegrationTest { private static final String METADATABITSTREAM_ENDPOINT = "/bitstream/"; private static final String METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/handle"; private static final String METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/allzip"; @@ -94,7 +84,8 @@ public void downloadSingleFileNullPathVariable() throws Exception { public void downloadSingleFileWithAuthorize() throws Exception { InputStream ip = bitstreamService.retrieve(context, bts); String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + + "/" + publicItem.getHandle() + "/" + bts.getName())) .andExpect(status().isOk()) .andExpect(content().contentType("application/octet-stream;charset=UTF-8")) .andExpect(content().bytes(IOUtils.toByteArray(ip))); @@ -102,7 +93,8 @@ public void downloadSingleFileWithAuthorize() throws Exception { @Test public void downloadSingleFileWithNoAuthorize() throws Exception { - getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + + "/" + publicItem.getHandle() + "/" + bts.getName())) .andExpect(status().isOk()); } @@ -121,7 +113,8 @@ public void downloadAllZip() throws Exception { zip.close(); String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT ).param("handleId", publicItem.getHandle())) + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT ).param("handleId", + publicItem.getHandle())) .andExpect(status().isOk()) .andExpect(content().bytes(byteArrayOutputStream.toByteArray())); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index 7f8e924f832b..c0de3540fc42 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -7,16 +7,30 @@ */ package org.dspace.app.rest; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; + import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.app.util.Util; import org.dspace.authorize.service.AuthorizeService; -import org.dspace.builder.*; -import org.dspace.content.*; -import org.dspace.content.clarin.ClarinLicense; -import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Collection; +import org.dspace.content.Item; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.util.FileTreeViewGenerator; @@ -25,20 +39,11 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.sql.SQLException; -import java.util.List; - -import static org.hamcrest.Matchers.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; - public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegrationTest { private static final String METADATABITSTREAM_ENDPOINT = "/api/core/metadatabitstream/"; - private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "search/byHandle"; + private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = + METADATABITSTREAM_ENDPOINT + "search/byHandle"; private static final String FILE_GRP_TYPE = "ORIGINAL"; private static final String AUTHOR = "Test author name"; private Collection col; @@ -104,9 +109,11 @@ public void findByHandle() throws Exception { .andExpect(jsonPath("$._embedded.metadatabitstreams[*].description") .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormatDescription(context))))) .andExpect(jsonPath("$._embedded.metadatabitstreams[*].format") - .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormat(context).getMIMEType())))) + .value(Matchers.containsInAnyOrder(Matchers.containsString( + bts.getFormat(context).getMIMEType())))) .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileSize") - .value(Matchers.containsInAnyOrder(Matchers.containsString(FileTreeViewGenerator.humanReadableFileSize(bts.getSizeBytes()))))) + .value(Matchers.containsInAnyOrder(Matchers.containsString( + FileTreeViewGenerator.humanReadableFileSize(bts.getSizeBytes()))))) .andExpect(jsonPath("$._embedded.metadatabitstreams[*].canPreview") .value(Matchers.containsInAnyOrder(Matchers.is(canPreview)))) .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileInfo").exists()) @@ -128,7 +135,9 @@ public void findByHandleEmptyFileGrpType() throws Exception { .andExpect(jsonPath("$.page.totalPages", is(0))) .andExpect(jsonPath("$.page.size", is(20))) .andExpect(jsonPath("$.page.number", is(0))) - .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + "?handle=" + publicItem.getHandle() + "&fileGrpType="))); + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + + "?handle=" + publicItem.getHandle() + "&fileGrpType="))); } @Test @@ -152,7 +161,7 @@ private void composeURL() { } else { identifier = "id/" + bts.getID(); } - url = "/bitstream/"+identifier+"/"; + url = "/bitstream/" + identifier + "/"; try { if (bts.getName() != null) { url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); @@ -165,7 +174,7 @@ private void composeURL() { if (authorizeService.authorizeActionBoolean(context, bts, Constants.READ)) { isAllowed = "y"; } - } catch (SQLException e) {/* Do nothing */} + } catch (SQLException e) { /* Do nothing */ } url += "&isAllowed=" + isAllowed; } From c45960d7a93f20b7da8b7652d48f189b9006dee9 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Wed, 4 Oct 2023 12:46:13 +0200 Subject: [PATCH 10/10] Updated endpoints and fixed tests --- .../dspace/app/rest/MetadataBitstreamController.java | 5 +++-- .../repository/MetadataBitstreamRestRepository.java | 10 ++++++++-- .../dspace/app/rest/MetadataBitstreamControllerIT.java | 4 ++-- .../app/rest/MetadataBitstreamRestRepositoryIT.java | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 7334d894bbd5..ac26a2c69551 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -1,4 +1,4 @@ -/** + /** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at @@ -24,6 +24,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.MissingLicenseAgreementException; @@ -54,7 +55,7 @@ * This CLARIN Controller download a single file or a ZIP file from the Item's bitstream. */ @RestController -@RequestMapping("/bitstream") +@RequestMapping("/api/" + BitstreamRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME) public class MetadataBitstreamController { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamController.class); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 2fbd4731fb9f..4f4ca065cdaa 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -36,6 +36,7 @@ import org.dspace.app.rest.converter.MetadataBitstreamWrapperConverter; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; import org.dspace.app.rest.model.wrapper.MetadataBitstreamWrapper; import org.dspace.app.util.Util; @@ -199,7 +200,11 @@ private List getFilePreviewContent(Context context, Bitstream bitstrea } catch (MissingLicenseAgreementException e) { /* Do nothing */ } if (Objects.nonNull(inputStream)) { - fileInfos = processInputStreamToFilePreview(context, bitstream, fileInfos, inputStream); + try { + fileInfos = processInputStreamToFilePreview(context, bitstream, fileInfos, inputStream); + } catch (IllegalStateException e) { + log.error("Cannot process Input Stream to file preview because: " + e.getMessage()); + } } return fileInfos; } @@ -252,7 +257,8 @@ private String composePreviewURL(Context context, Item item, Bitstream bitstream } else { identifier = "id/" + bitstream.getID(); } - String url = contextPath + "/bitstream/" + identifier; + String url = contextPath + "/api/" + BitstreamRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME + "/" + + identifier; try { if (bitstream.getName() != null) { url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java index fbf76442b18f..d397062e0200 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -33,7 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired; public class MetadataBitstreamControllerIT extends AbstractControllerIntegrationTest { - private static final String METADATABITSTREAM_ENDPOINT = "/bitstream/"; + private static final String METADATABITSTREAM_ENDPOINT = "/api/core/bitstreams/"; private static final String METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/handle"; private static final String METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/allzip"; private static final String AUTHOR = "Test author name"; @@ -77,7 +77,7 @@ public void setUp() throws Exception { @Test public void downloadSingleFileNullPathVariable() throws Exception { - getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)).andExpect(status().isNotFound()); + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)).andExpect(status().is4xxClientError()); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index c0de3540fc42..94f98998b8d3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -161,7 +161,7 @@ private void composeURL() { } else { identifier = "id/" + bts.getID(); } - url = "/bitstream/" + identifier + "/"; + url = "/api/core/bitstreams/" + identifier + "/"; try { if (bts.getName() != null) { url += Util.encodeBitstreamName(bts.getName(), "UTF-8");