org.goobi.presentation.contentservlet.controller.GetMetsPdfAction.java Source code

Java tutorial

Introduction

Here is the source code for org.goobi.presentation.contentservlet.controller.GetMetsPdfAction.java

Source

/*
 * This file is part of the ContentServer project.
 * Visit the websites for more information. 
 *       - http://gdz.sub.uni-goettingen.de 
 *       - http://www.intranda.com 
 *       - http://www.digiverso.com
 * 
 * Copyright 2009, Center for Retrospective Digitization, Gttingen (GDZ),
 * intranda software
 *
 * This is the extended version updated by intranda
 * Copyright 2012, intranda GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.goobi.presentation.contentservlet.controller;

import gov.loc.mets.DivType;
import gov.loc.mets.DivType.Mptr;

import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.ExceptionConverter;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.PdfWriter;

import de.unigoettingen.sub.commons.contentlib.exceptions.CacheException;
import de.unigoettingen.sub.commons.contentlib.exceptions.ContentLibException;
import de.unigoettingen.sub.commons.contentlib.exceptions.ContentLibPdfException;
import de.unigoettingen.sub.commons.contentlib.exceptions.WatermarkException;
import de.unigoettingen.sub.commons.contentlib.imagelib.ContentLibUtil;
import de.unigoettingen.sub.commons.contentlib.imagelib.ImageManipulator;
import de.unigoettingen.sub.commons.contentlib.imagelib.Watermark;
import de.unigoettingen.sub.commons.contentlib.pdflib.PDFManager;
import de.unigoettingen.sub.commons.contentlib.pdflib.PDFManager.PdfPageSize;
import de.unigoettingen.sub.commons.contentlib.pdflib.PDFTitlePage;
import de.unigoettingen.sub.commons.contentlib.pdflib.PDFTitlePageLine;
import de.unigoettingen.sub.commons.contentlib.servlet.ServletWatermark;
import de.unigoettingen.sub.commons.contentlib.servlet.Util;
import de.unigoettingen.sub.commons.contentlib.servlet.controller.Action;
import de.unigoettingen.sub.commons.contentlib.servlet.controller.ContentServer;
import de.unigoettingen.sub.commons.contentlib.servlet.model.ContentServerConfiguration;
import de.unigoettingen.sub.commons.simplemets.METSParser;
import de.unigoettingen.sub.commons.simplemets.SimplePDFMetadataExtractor;
import de.unigoettingen.sub.commons.simplemets.SimpleStructureMetadataExtractor;
import de.unigoettingen.sub.commons.util.datasource.Structure;
import de.unigoettingen.sub.commons.util.datasource.UrlImage;

/************************************************************************************
 * pdf action for all kinds of simple pdf handlings first of all validate all request parameters, and than interprete all request parameters for
 * correct image handling
 * 
 * @version 02.01.2009
 * @author Steffen Hankiewicz
 ************************************************************************************/
public class GetMetsPdfAction implements Action {
    private static final Logger LOGGER = Logger.getLogger(GetMetsPdfAction.class);
    private Watermark myWatermark = null;

    /************************************************************************************
     * exectute mets handling and generation of pdf file and send pdf back to output stream of the servlet, after setting correct mime type
     * 
     * @param request {@link HttpServletRequest} of ServletRequest
     * @param response {@link HttpServletResponse} for writing to response output stream
     * @throws IOException
     * @throws ServletException
     * @throws ContentLibException
     * @throws URISyntaxException
     * @throws CacheException 
     ************************************************************************************/
    @Override
    public void run(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException, ContentLibException, URISyntaxException, CacheException {

        /* first of all validation */
        validateParameters(request);

        /*
         * -------------------------------- get central configuration and retrieve source image from url --------------------------------
         */
        ContentServerConfiguration config = ContentServerConfiguration.getInstance();
        if (config.getWatermarkUse()) {
            File watermarkfile = new File(new URI(config.getWatermarkConfigFilePath()));
            myWatermark = Watermark.generateWatermark(request, watermarkfile);
        }
        OutputStream myOutStream = response.getOutputStream();
        ContentCache cc = ContentServer.getPdfCache();
        String myUniqueID = getContentCacheIdForRequest(request, config);
        setTargetNameAndMimeType(request, response, config);
        try {
            /*
             * -------------------------------- ask ContentCache, if object already exists --------------------------------
             */
            PDFManager pdfmanager = null;
            try {
                boolean ignoreCache = false;
                /* check if cache should be ignored */
                if (request.getParameter("ignoreCache") != null) {
                    String ignore = request.getParameter("ignoreCache").trim();
                    ignoreCache = Boolean.parseBoolean(ignore);
                }
                if (cc == null || !config.getPdfCacheUse()) {
                    ignoreCache = true;
                    cc = null;
                    LOGGER.debug("cache deactivated via configuration");
                }

                /* if cache should not be ignored and cache contains file, write it back to stream */
                if (!ignoreCache && cc.cacheContains(myUniqueID, "pdf")) {
                    LOGGER.debug("get file from cache: " + myUniqueID);
                    cc.writeToStream(response.getOutputStream(), myUniqueID, "pdf");
                    return;
                } else if (ignoreCache == false) {
                    LOGGER.debug("file not found in cache: " + myUniqueID);
                }

                /*
                 * -------------------------------- if Cache is not used, parse mets file name and add it to repository path
                 * --------------------------------
                 */
                String metsFile = request.getParameter("metsFile");
                if (!metsFile.endsWith(".xml")) {
                    metsFile += ".xml";
                }
                URL fullMetsPath = new URL(config.getRepositoryPathMets() + metsFile);
                LOGGER.debug("mets file to parse: " + fullMetsPath);

                /*
                 * -------------------------------- open METS file --------------------------------
                 */
                METSParser metsparser = new METSParser(fullMetsPath, true);
                SimplePDFMetadataExtractor spme = new SimplePDFMetadataExtractor();
                spme.activateDFGConfiguration();

                // set the Metadata extractor and the Bookmark metadata extractor
                metsparser.setMetadataextractor(spme);
                metsparser.setStructureMetadataExtractor(new SimpleStructureMetadataExtractor());

                /*
                 * -------------------------------- check if filegroup is defined in request parameter, else take it from config file
                 * --------------------------------
                 */
                String strMetsFileGroup = request.getParameter("metsFileGroup");
                if (strMetsFileGroup == null) {
                    strMetsFileGroup = config.getDefaultMetsFileGroup();
                }
                metsparser.setFilegroupsuseattributevalue(strMetsFileGroup);
                LOGGER.debug("Using METS file group: " + strMetsFileGroup);

                /*
                 * -------------------------------- check divID as parameter, else use upper most divID --------------------------------
                 */
                String divID = request.getParameter("divID");
                DivType pdfdiv = null;
                if (StringUtils.isNotBlank(divID)) {
                    pdfdiv = metsparser.getDIVbyID(divID);
                    // check if the METS file is a monograph, volume or multivolume file
                } else {
                    DivType uplogdiv = metsparser.getUppermostLogicalDiv();
                    if (uplogdiv == null) {
                        throw new ContentLibPdfException("Can't create PDF; div seems to be an anchor.");
                    }

                    // check, if we have <mptr> as children
                    List<Mptr> mptrs = uplogdiv.getMptrList();
                    if ((mptrs == null) || (mptrs.size() == 0)) {
                        // no mptr - must be a monograph
                        // in this case the uppermost logical id is the one we are looking for
                        pdfdiv = uplogdiv;
                    } else {
                        // check, if we have a physical structmap
                        DivType physDiv = metsparser.getUppermostPhysicalDiv();
                        if (physDiv == null) {
                            // it is a multivolume or a periodical or anything like this
                            // in this case the uppermost logical div is the one for which we create the PDF
                            pdfdiv = uplogdiv;
                        } else {
                            // it is the first child div; this represents the volume

                            List<DivType> children = uplogdiv.getDivList();
                            if ((children == null) || (children.size() == 0)) {
                                throw new ContentLibPdfException("Can't create PDF; can't find a div");
                            }
                            pdfdiv = children.get(0); // the first child
                        }
                    }
                }
                spme.calculateMetadata(pdfdiv, metsparser);
                metsparser.getAllFilesForRelatedDivs(pdfdiv.getID()); // get page names

                /*
                 * -------------------------------- get list of files and pagenames --------------------------------
                 */
                Map<Integer, UrlImage> myPages = metsparser.getImageMap();
                // HashMap<Integer, URL> myURLs = metsparser.getPageUrls();
                Map<Integer, String> myNames = metsparser.getPageNames();
                List<? extends Structure> pDFBookmarks = metsparser.getStructureList();
                // PDFManager pdfmanager = new PDFManager(myURLs);
                pdfmanager = new PDFManager(myPages, true);

                setPdfManagerDefaults(request, config, pdfmanager);

                /*
                 * -------------------------------- set pdf meta data --------------------------------
                 */
                if (spme.getPdftitle() != null) {
                    String title = spme.getPdftitle().trim();
                    pdfmanager.setTitle(title);
                }
                if (spme.getPdfcreator() != null) {
                    String creator = spme.getPdfcreator().trim();
                    pdfmanager.setCreator(creator);
                    pdfmanager.setAuthor(creator);
                }
                if (spme.getPdfkeywords() != null) {
                    pdfmanager.setSubject(spme.getPdfkeywords());
                }

                /*
                 * -------------------------------- if configured create PDF title page - either read its path from config file and fill it with mets
                 * content - or if given as url parameter use it directly without any change --------------------------------
                 */
                if (config.getPdfTitlePageUse()) {
                    PDFTitlePage pdftp = new PDFTitlePage();

                    String pdfconfigurl = request.getParameter("pdftitlepage");
                    if (StringUtils.isNotBlank(pdfconfigurl)) {
                        pdftp.readConfiguration(new URI(pdfconfigurl));
                    } else {
                        pdftp.readConfiguration(config.getPdfTitlePageConfigFile());
                        if (spme.getPdfTitlepageLine1() != null) {
                            PDFTitlePageLine ptl = new PDFTitlePageLine(spme.getPdfTitlepageLine1());
                            ptl.setLinetype(2);
                            ptl.setFontsize(14);
                            pdftp.addPDFTitlePageLine(ptl);
                        }

                        if (spme.getPdfTitlepageLine2() != null) {
                            PDFTitlePageLine ptl = new PDFTitlePageLine(spme.getPdfTitlepageLine2());
                            ptl.setLinetype(2);
                            ptl.setFontsize(10);
                            pdftp.addPDFTitlePageLine(ptl);
                        }

                        if (spme.getPdfTitlepageLine3() != null) {
                            PDFTitlePageLine ptl = new PDFTitlePageLine(spme.getPdfTitlepageLine3());
                            ptl.setLinetype(2);
                            ptl.setFontsize(10);
                            pdftp.addPDFTitlePageLine(ptl);
                        }

                        if (spme.getPdfTitlepageLine4() != null) {
                            PDFTitlePageLine ptl = new PDFTitlePageLine(spme.getPdfTitlepageLine4());
                            ptl.setLinetype(2);
                            ptl.setFontsize(10);
                            pdftp.addPDFTitlePageLine(ptl);
                        }
                    }
                    pdfmanager.setPdftitlepage(pdftp);
                }

                /*
                 * -------------------------------- set page names and bookmarks --------------------------------
                 */
                if ((myNames != null) && (myNames.size() > 0)) {
                    pdfmanager.setImageNames(myNames);
                }
                if ((pDFBookmarks != null) && (pDFBookmarks.size() > 0)) {
                    pdfmanager.setStructureList(pDFBookmarks);
                }

                /*
                 * -------------------------------- write pdf to response stream (and cache) --------------------------------
                 */
                /* remove file from cache, if cache should be used and file present */
                if (cc != null) {
                    cc.delete(myUniqueID, "pdf");
                }
                /* if cache size is exceeded write it to response stream only */
                if (cc != null && !cc.isCacheSizeExceeded()) {
                    LOGGER.info("write file to cache and servlet response: " + cc.getFileForId(myUniqueID, "pdf"));
                    myOutStream = new CacheOutputStream(cc.getFileForId(myUniqueID, "pdf"),
                            response.getOutputStream());
                } else if (cc == null) {
                    LOGGER.info("file will not be written to cache, cache is deactivated in configuration");
                } else if (cc.isCacheSizeExceeded()) {
                    LOGGER.info(
                            "file will not be written to cache, maximum cache size exceeded defined configuration");
                }
            } catch (NullPointerException e) {
                throw new NullPointerException("Nullpointer occured before pdf-generation");
            }
            /* write to stream */
            if (pdfmanager != null) {
                pdfmanager.createPDF(myOutStream, getPageSize(request), myWatermark);
            }
        } catch (Exception e) {
            LOGGER.error("error during pdf generation (" + e.getClass().getName() + ")", e);
            Document pdfdoc = new Document();
            PdfWriter writer;
            try {
                writer = PdfWriter.getInstance(pdfdoc, myOutStream);
            } catch (DocumentException e1) {
                throw new ContentLibException("wrapped DocumentException", e1);
            }
            pdfdoc.open();

            /*---------------------------------
             * Generate Watermark
             *---------------------------------*/
            String errorString = e.getClass().getName() + ": " + e.getMessage();
            String jpgfile = new File(Util.getBaseFolderAsFile(), "errorfile.jpg").getAbsolutePath();
            LOGGER.debug("errorfile to embedd: " + jpgfile);
            FileInputStream inputFileStream = new FileInputStream(jpgfile);
            RenderedImage ri = ServletWatermark.generateErrorWatermark(inputFileStream, errorString)
                    .getRenderedImage();

            /*
             * -------------------------------- prepare target and read created image --------------------------------
             */
            BufferedImage buffImage = ImageManipulator.fromRenderedToBuffered(ri);
            Image pdfImage;
            try {
                pdfImage = Image.getInstance(buffImage, null, false);
                pdfdoc.add(pdfImage);
            } catch (BadElementException e1) {
                LOGGER.error("error while adding pdfImage", e);
                throw new ContentLibException("wrapped BadElementException", e1);
            } catch (DocumentException e2) {
                LOGGER.error("error while adding pdfImage", e);
                throw new ContentLibException("wrapped DocumentException", e2);
            } finally {
                /*
                 * -------------------------------- close all --------------------------------
                 */
                try {
                    if (inputFileStream != null) {
                        inputFileStream.close();
                    }
                    if (pdfdoc != null) {
                        pdfdoc.close();
                    }
                    if (writer != null) {
                        writer.close();
                    }
                } catch (ExceptionConverter e2) {
                    LOGGER.warn("Caught ExceptionConverter object");
                } finally {

                    /* on errors remove incomplete file from cache */
                    try {
                        if (myOutStream != null) {
                            myOutStream.flush();
                            myOutStream.close();
                        }
                    } catch (Exception e2) {
                        LOGGER.debug("Caught unknown Exception");
                    }
                    if (cc != null && cc.cacheContains(myUniqueID, "pdf")) {
                        cc.delete(myUniqueID, "pdf");
                    }
                }
            }
        } finally {
            if (myOutStream != null) {
                myOutStream.flush();
                myOutStream.close();
            }
        }
    }

    /************************************************************************************
     * set some properties depending on config file
     * 
     * @param request {@link HttpServletRequest} of ServletRequest
     * @param config instance of ContentServerConfiguration
     * @param pdfmanager {@link PDFManager} where to set the properties
     * @throws IOException
     ************************************************************************************/
    protected void setPdfManagerDefaults(HttpServletRequest request, ContentServerConfiguration config,
            PDFManager pdfmanager) throws IOException {

        pdfmanager.setAlwaysUseRenderedImage(config.getPdfDefaultAlwaysUseRenderedImage());
        pdfmanager.setAlwaysCompressToJPEG(config.getPdfDefaultAlwaysCompressToJPEG());

        if (config.getWatermarkUse()) {
            try {
                File watermarkfile = new File(new URI(config.getWatermarkConfigFilePath()));
                if (request.getParameterMap().containsKey("watermarkText")) {
                    myWatermark = new Watermark(watermarkfile, request.getParameter("watermarkText"));
                } else {
                    myWatermark = new Watermark(watermarkfile);
                }
            } catch (WatermarkException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (URISyntaxException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        /*
         * -------------------------------- set ICC profile --------------------------------
         */

        File iccfile = new File(Util.getBaseFolderAsFile(), "sRGB.icc");
        ICC_Profile icc = null;

        if (!iccfile.exists()) {
            InputStream is = GetMetsPdfAction.class.getResourceAsStream("sRGB.icc");
            if (is != null) {
                icc = ICC_Profile.getInstance(is);
            }
        } else {
            icc = ICC_Profile.getInstance(iccfile.getAbsolutePath());
        }

        if (icc != null) {
            pdfmanager.setIccprofile(icc);
        } else {
            LOGGER.error("No ICC profile given!");
        }

        /*
         * -------------------------------- check if pdf should be written as pdf/A, if configured in request parameter, else take it from config file
         * --------------------------------
         */
        boolean pdfa = Boolean.parseBoolean(getParameterFromRequestOrConfig("writeAsPdfA", request));
        pdfmanager.setPdfa(pdfa);
    }

    /************************************************************************************
     * set pagesize of pdf file depending on request parameter or config
     * 
     * @param request {@link HttpServletRequest} of ServletRequest
     ************************************************************************************/
    protected PdfPageSize getPageSize(HttpServletRequest request) {
        /*
         * -------------------------------- check if page size is defined in request else take it from config file --------------------------------
         */
        String strPageSize = getParameterFromRequestOrConfig("pagesize", request);

        return PDFManager.getPageSizefromString(strPageSize);
    }

    /************************************************************************************
     * set target file name and mime type for response depending on request and config
     * 
     * @param request {@link HttpServletRequest} of ServletRequest
     * @param responst {@link HttpServletResponse} of ServletResponse
     * @param config instance of ContentServerConfiguration
     ************************************************************************************/
    protected void setTargetNameAndMimeType(HttpServletRequest request, HttpServletResponse response,
            ContentServerConfiguration config) {
        /*
         * -------------------------------- set file name and attachment header from parameter or from configuration --------------------------------
         */
        StringBuilder targetFileName = new StringBuilder();
        if (config.getSendPdfAsAttachment()) {
            targetFileName.append("attachment; ");
        }
        targetFileName.append("filename=");
        if (request.getParameter("targetFileName") != null) {
            targetFileName.append(request.getParameter("targetFileName"));
        } else {
            String filename = ContentLibUtil.getCustomizedFileName(config.getDefaultFileNamePdf(), ".pdf");
            targetFileName.append(filename);
        }
        response.setHeader("Content-Disposition", targetFileName.toString());
        response.setContentType("application/pdf");
    }

    /************************************************************************************
     * validate all parameters of request for mets handling, throws IllegalArgumentException if one request parameter is not valid
     * 
     * @param request {@link HttpServletRequest} of ServletRequest
     * @throws IllegalArgumentException
     ************************************************************************************/
    @Override
    public void validateParameters(HttpServletRequest request) throws IllegalArgumentException {
        ContentServerConfiguration config = ContentServerConfiguration.getInstance();

        /* validate repository */
        if (config.getRepositoryPathMets() == null) {
            throw new IllegalArgumentException("no repository url defined");
        }

        /* if divID is used it can not be empty */
        if (request.getParameter("divID") != null) {
            if (StringUtils.isBlank(request.getParameter("divID"))) {
                throw new IllegalArgumentException("used parameter divID can not be empty");
            }
        }

        /* validate pagesize */
        // TODO: Look a the method getPageSize, this shouldn't be here
        String strPageSize = request.getParameter("pagesize");
        LOGGER.debug("Page size is " + strPageSize);
        if (strPageSize != null && !ContentLibUtil.getAllPdfSizesAsList().contains(strPageSize)) {
            throw new IllegalArgumentException("unknown pagesize used; value has be one of: "
                    + ContentLibUtil.getAllPdfSizesAsList().toString());
        }

        /* metsFile has to be not blank */
        if (StringUtils.isBlank(request.getParameter("metsFile"))) {
            throw new IllegalArgumentException("parameter metsFile can not be null or empty");
        }
        LOGGER.debug("METS file " + request.getParameter("metsFile"));
    }

    /*************************************************************************************
     * generate an ID for a pdf file, to cache it under an unique name
     * 
     * @param request the current {@link HttpServletRequest}
     * @param inConfig current internal {@link ContentServerConfiguration} objekt
     ************************************************************************************/
    private String getContentCacheIdForRequest(HttpServletRequest request, ContentServerConfiguration inConfig) {
        String myId = request.getParameter("metsFile");
        if (request.getParameter("divID") != null) {
            myId += "_" + request.getParameter("divID").trim();
        }

        Boolean useShortFileNames = false;
        try {
            useShortFileNames = inConfig.getContentCacheUseShortFileNames();
        } catch (NoSuchElementException e) {
            LOGGER.warn("Missing configuration for contentCache[@useShortFileNames]", e);
        }

        if (useShortFileNames) {
            myId += "_pdfA" + getParameterFromRequestOrConfig("writeAsPdfA", request);
            myId += "_" + getParameterFromRequestOrConfig("metsFileGroup", request);
            myId += "_" + getParameterFromRequestOrConfig("pdftitlepage", request);
            myId += "_" + getParameterFromRequestOrConfig("pagesize", request);
        }
        return myId;
    }

    /*************************************************************************************
     * get parameter either from request or else the default value from config file
     * 
     * @param inParam the requested parameter
     * @return the value from request or config file
     ************************************************************************************/
    protected String getParameterFromRequestOrConfig(String inParam, HttpServletRequest request) {
        /* take it from request if present */
        String value = request.getParameter(inParam);
        if (StringUtils.isNotBlank(value)) {
            return value.trim();
        } else {
            ContentServerConfiguration config = ContentServerConfiguration.getInstance();
            /* if not in request given, take it from config file */
            if (inParam.equals("writeAsPdfA")) {
                return config.getPdfDefaultWritePdfA().toString();
            }

            if (inParam.equals("metsFileGroup")) {
                return config.getDefaultMetsFileGroup();
            }
            if (inParam.equals("pagesize")) {
                return config.getPdfDefaultPageSize();
            }
        }
        return "";
    }

}