71734: Sitemap REST controller

This commit is contained in:
Marie Verdonck
2020-07-08 14:19:54 +02:00
parent 793473663f
commit 56be1bbe74

View File

@@ -0,0 +1,148 @@
/**
* 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 java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javassist.NotFoundException;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.MultipartFileSender;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This is a specialized controller to provide access to the sitemap files, generated by
* {@link org.dspace.app.sitemap.GenerateSitemaps}
*
* The mapping for requested endpoint try to resolve a valid sitemap file name, for example
* <pre>
* {@code
* https://<dspace.server.url>/api/discover/sitemaps/26453b4d-e513-44e8-8d5b-395f62972eff/sitemap0.html
* }
* </pre>
*
* @author Maria Verdonck (Atmire) on 08/07/2020
*/
@RestController
@RequestMapping("/api/" + RestModel.DISCOVER + "/sitemaps")
public class SitemapRestController {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SitemapRestController.class);
@Autowired
ConfigurationService configurationService;
// Most file systems are configured to use block sizes of 4096 or 8192 and our buffer should be a multiple of that.
private static final int BUFFER_SIZE = 4096 * 10;
/**
* Tries to retrieve a matching sitemap file in configured location
*
* @param name the name of the requested sitemap file
* @param response the HTTP response
* @param request the HTTP request
* @throws NotFoundException if no matching sitemap file can be found
* @throws SQLException if db error while completing DSpace context
* @throws IOException if IO error surrounding sitemap file
*/
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}, value = "/{name}")
public void retrieve(@PathVariable String name, HttpServletResponse response,
HttpServletRequest request) throws NotFoundException, IOException, SQLException {
// Find sitemap with given name in dspace/sitemaps
File foundSitemapFile = null;
File sitemapOutputDir = new File(configurationService.getProperty("sitemap.dir"));
if (sitemapOutputDir.exists() && sitemapOutputDir.isDirectory()) {
// List of all files and directories inside sitemapOutputDir
File sitemapFilesList[] = sitemapOutputDir.listFiles();
for (File sitemapFile : sitemapFilesList) {
if (name.equalsIgnoreCase(sitemapFile.getName())) {
if (sitemapFile.isFile()) {
foundSitemapFile = sitemapFile;
} else {
throw new NotFoundException(
"Directory with name " + name + " in " + sitemapOutputDir.getAbsolutePath() +
" found, but no file");
}
}
}
} else {
throw new NotFoundException("Sitemap directory in " + sitemapOutputDir.getAbsolutePath() + " does not " +
"exist, either sitemaps have not been generated, or are located elsewhere.");
}
if (foundSitemapFile == null) {
throw new NotFoundException(
"Could not find sitemap file with name " + name + " in " + sitemapOutputDir.getAbsolutePath());
} else {
// return found sitemap file
this.returnSitemapFile(foundSitemapFile, response, request);
}
}
/**
* Sends back the matching sitemap file as a MultipartFile, with the headers set with details of the file
* (content, size, name, last modified)
*
* @param foundSitemapFile the found sitemap file, with matching name as in request path
* @param response the HTTP response
* @param request the HTTP request
* @throws SQLException if db error while completing DSpace context
* @throws IOException if IO error surrounding sitemap file
*/
private void returnSitemapFile(File foundSitemapFile, HttpServletResponse response, HttpServletRequest request)
throws SQLException, IOException {
// Pipe the bits
try (InputStream is = new FileInputStream(foundSitemapFile)) {
MultipartFileSender sender = MultipartFileSender
.fromInputStream(is)
.withBufferSize(BUFFER_SIZE)
.withFileName(foundSitemapFile.getName())
.withLength(foundSitemapFile.length())
.withMimetype(Files.probeContentType(foundSitemapFile.toPath()))
.with(request)
.with(response);
sender.withLastModified(foundSitemapFile.lastModified());
// Determine if we need to send the file as a download or if the browser can open it inline
long dispositionThreshold = configurationService.getLongProperty("webui.content_disposition_threshold");
if (dispositionThreshold >= 0 && foundSitemapFile.length() > dispositionThreshold) {
sender.withDisposition(MultipartFileSender.CONTENT_DISPOSITION_ATTACHMENT);
}
Context context = ContextUtil.obtainContext(request);
// We have all the data we need, close the connection to the database so that it doesn't stay open during
// download/streaming
context.complete();
// Send the data
if (sender.isValid()) {
sender.serveResource();
}
} catch (ClientAbortException e) {
log.debug("Client aborted the request before the download was completed. " +
"Client is probably switching to a Range request.", e);
}
}
}