All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mycore.services.zipper.MCRCompressServlet Maven / Gradle / Ivy

There is a newer version: 2024.05
Show newest version
/*
 * This file is part of ***  M y C o R e  ***
 * See http://www.mycore.de/ for details.
 *
 * MyCoRe is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MyCoRe is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MyCoRe.  If not, see .
 */

package org.mycore.services.zipper;

import static org.mycore.access.MCRAccessManager.PERMISSION_READ;
import static org.mycore.common.MCRConstants.XLINK_NAMESPACE;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Element;
import org.mycore.access.MCRAccessManager;
import org.mycore.common.MCRException;
import org.mycore.common.content.MCRContent;
import org.mycore.common.content.transformer.MCRContentTransformer;
import org.mycore.common.content.transformer.MCRParameterizedTransformer;
import org.mycore.common.xml.MCRLayoutService;
import org.mycore.common.xml.MCRXMLFunctions;
import org.mycore.common.xsl.MCRParameterCollector;
import org.mycore.datamodel.common.MCRISO8601Date;
import org.mycore.datamodel.common.MCRXMLMetadataManager;
import org.mycore.datamodel.metadata.MCRObjectID;
import org.mycore.datamodel.niofs.MCRPath;
import org.mycore.frontend.servlets.MCRServlet;
import org.mycore.frontend.servlets.MCRServletJob;

/**
 * This servlet delivers the contents of MCROjects to the client in
 * container files (see classes extending this servlet). Read permission is required.
 * There are three modes
 * 
    *
  1. if id=mycoreobjectID (delivers the metadata, including all derivates)
  2. *
  3. if id=derivateID (delivers all files of the derivate)
  4. *
  5. if id=derivateID/directoryPath (delivers all files in the given directory of the derivate)
  6. *
* * "id" maybe specified as {@link HttpServletRequest#getPathInfo()} or as * {@link MCRServlet#getProperty(HttpServletRequest, String)}. * @author Thomas Scheffler (yagee) * */ public abstract class MCRCompressServlet extends MCRServlet { private static final long serialVersionUID = 1L; protected static String KEY_OBJECT_ID = MCRCompressServlet.class.getCanonicalName() + ".object"; protected static String KEY_PATH = MCRCompressServlet.class.getCanonicalName() + ".path"; private static Pattern PATH_INFO_PATTERN = Pattern.compile("\\A([\\w]+)/([\\w/]+)\\z"); private static Logger LOGGER = LogManager.getLogger(MCRCompressServlet.class); @Override protected void think(MCRServletJob job) throws Exception { HttpServletRequest req = job.getRequest(); //id parameter for backward compatibility String paramid = getProperty(req, "id"); if (paramid == null) { String pathInfo = req.getPathInfo(); if (pathInfo != null) { paramid = pathInfo.substring(1); } } if (paramid == null) { job.getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST, "What should I do?"); return; } Matcher ma = PATH_INFO_PATTERN.matcher(paramid); //path & directory MCRObjectID id; String path; try { if (ma.find()) { id = MCRObjectID.getInstance(ma.group(1)); path = ma.group(2); } else { id = MCRObjectID.getInstance(paramid); path = null; } } catch (MCRException e) { String objId = ma.find() ? ma.group(1) : paramid; job.getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST, "ID is not valid: " + objId); return; } boolean readPermission = id.getTypeId().equals("derivate") ? MCRAccessManager .checkDerivateContentPermission(id, PERMISSION_READ) : MCRAccessManager.checkPermission(id, PERMISSION_READ); if (!readPermission) { job.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN, "You may not read " + id); return; } req.setAttribute(KEY_OBJECT_ID, id); req.setAttribute(KEY_PATH, path); } @Override protected void render(MCRServletJob job, Exception ex) throws Exception { if (ex != null) { //we cannot handle it ourself throw ex; } if (job.getResponse().isCommitted()) { return; } MCRObjectID id = (MCRObjectID) job.getRequest().getAttribute(KEY_OBJECT_ID); String path = (String) job.getRequest().getAttribute(KEY_PATH); try (ServletOutputStream sout = job.getResponse().getOutputStream()) { StringBuffer requestURL = job.getRequest().getRequestURL(); if (job.getRequest().getQueryString() != null) { requestURL.append('?').append(job.getRequest().getQueryString()); } MCRISO8601Date mcriso8601Date = new MCRISO8601Date(); mcriso8601Date.setDate(new Date()); String comment = "Created by " + requestURL + " at " + mcriso8601Date.getISOString(); try (T container = createContainer(sout, comment)) { job.getResponse().setContentType(getMimeType()); String filename = getFileName(id, path); job.getResponse().addHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); if (id.getTypeId().equals("derivate")) { sendDerivate(id, path, container); } else { sendObject(id, job, container); } disposeContainer(container); } } } private void sendObject(MCRObjectID id, MCRServletJob job, T container) throws Exception { MCRContent content = MCRXMLMetadataManager.instance().retrieveContent(id); if (content == null) { throw new FileNotFoundException("Could not find object: " + id); } long lastModified = MCRXMLMetadataManager.instance().getLastModified(id); HttpServletRequest req = job.getRequest(); byte[] metaDataContent = getMetaDataContent(content, req); sendMetadataCompressed("metadata.xml", metaDataContent, lastModified, container); // zip all derivates List li = content.asXML().getRootElement().getChild("structure").getChild("derobjects") .getChildren("derobject"); for (Element el : li) { if (el.getAttributeValue("inherited").equals("0")) { String ownerID = el.getAttributeValue("href", XLINK_NAMESPACE); MCRObjectID derId = MCRObjectID.getInstance(ownerID); // here the access check is tested only against the derivate if (MCRAccessManager.checkDerivateContentPermission(derId, PERMISSION_READ) && MCRXMLFunctions.isDisplayedEnabledDerivate(ownerID)) { sendDerivate(derId, null, container); } } } } private byte[] getMetaDataContent(MCRContent content, HttpServletRequest req) throws Exception { // zip the object's Metadata MCRParameterCollector parameters = new MCRParameterCollector(req); if (parameters.getParameter("Style", null) == null) { parameters.setParameter("Style", "compress"); } MCRContentTransformer contentTransformer = MCRLayoutService.getContentTransformer(content.getDocType(), parameters); ByteArrayOutputStream out = new ByteArrayOutputStream(32 * 1024); if (contentTransformer instanceof MCRParameterizedTransformer) { ((MCRParameterizedTransformer) contentTransformer).transform(content, out, parameters); } else { contentTransformer.transform(content, out); } return out.toByteArray(); } private void sendDerivate(MCRObjectID id, String path, T container) throws IOException { MCRPath resolvedPath = MCRPath.getPath(id.toString(), path == null ? "/" : path); if (!Files.exists(resolvedPath)) { throw new NoSuchFileException(id.toString(), path, "Could not find path " + resolvedPath); } if (Files.isRegularFile(resolvedPath)) { BasicFileAttributes attrs = Files.readAttributes(resolvedPath, BasicFileAttributes.class); sendCompressedFile(resolvedPath, attrs, container); LOGGER.debug("file {} zipped", resolvedPath); return; } // root is a directory Files.walkFileTree(resolvedPath, new CompressVisitor<>(this, container)); } private String getFileName(MCRObjectID id, String path) { if (path == null || path.equals("")) { return new MessageFormat("{0}.{1}", Locale.ROOT).format(new Object[] { id, getFileExtension() }); } else { return new MessageFormat("{0}-{1}.{2}", Locale.ROOT).format( new Object[] { id, path.replaceAll("/", "-"), getFileExtension() }); } } /** * Constructs a path name in form of {ownerID}+'/'+{path} or {ownerID} if path is root component. * @param path absolute path */ protected String getFilename(MCRPath path) { return path.getNameCount() == 0 ? path.getOwner() : path.getOwner() + '/' + path.getRoot().relativize(path); } protected abstract void sendCompressedDirectory(MCRPath file, BasicFileAttributes attrs, T container) throws IOException; protected abstract void sendCompressedFile(MCRPath file, BasicFileAttributes attrs, T container) throws IOException; protected abstract void sendMetadataCompressed(String fileName, byte[] content, long modified, T container) throws IOException; protected abstract String getMimeType(); protected abstract String getFileExtension(); protected abstract T createContainer(ServletOutputStream sout, String comment); protected abstract void disposeContainer(T container) throws IOException; private static class CompressVisitor extends SimpleFileVisitor { private MCRCompressServlet impl; private T container; CompressVisitor(MCRCompressServlet impl, T container) { this.impl = impl; this.container = container; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { impl.sendCompressedDirectory(MCRPath.toMCRPath(dir), attrs, container); return super.preVisitDirectory(dir, attrs); } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { impl.sendCompressedFile(MCRPath.toMCRPath(file), attrs, container); LOGGER.debug("file {} zipped", file); return super.visitFile(file, attrs); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy