dk.dma.msiproxy.web.MessageDetailsServlet.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.msiproxy.web.MessageDetailsServlet.java

Source

/* Copyright (c) 2011 Danish Maritime Authority
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.dma.msiproxy.web;

import com.itextpdf.text.DocumentException;
import dk.dma.msiproxy.common.MsiProxyApp;
import dk.dma.msiproxy.common.provider.AbstractProviderService;
import dk.dma.msiproxy.common.provider.Providers;
import dk.dma.msiproxy.common.util.WebUtils;
import dk.dma.msiproxy.model.MessageFilter;
import dk.dma.msiproxy.model.msi.Area;
import dk.dma.msiproxy.model.msi.Message;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.w3c.dom.Document;
import org.w3c.tidy.Tidy;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Servlet used for generating either a HTML details page
 * or a PDF for the MSI details defined by the provider and language
 * specified using request parameters.
 */
@WebServlet(urlPatterns = { "/details.pdf", "/details.html" }, asyncSupported = true)
public class MessageDetailsServlet extends HttpServlet {

    private static final String DETAILS_JSP_FILE = "/WEB-INF/jsp/details.jsp";

    @Inject
    Logger log;

    @Inject
    Providers providers;

    @Inject
    MsiProxyApp app;

    /**
     * Main GET method
     * @param request servlet request
     * @param response servlet response
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        // Never cache the response
        response = WebUtils.nocache(response);

        // Read the request parameters
        String providerId = request.getParameter("provider");
        String lang = request.getParameter("lang");
        String messageId = request.getParameter("messageId");
        String activeNow = request.getParameter("activeNow");
        String areaHeadingIds = request.getParameter("areaHeadings");

        List<AbstractProviderService> providerServices = providers.getProviders(providerId);

        if (providerServices.size() == 0) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid 'provider' parameter");
            return;
        }

        // Ensure that the language is valid
        lang = providerServices.get(0).getLanguage(lang);

        // Force the encoding and the locale based on the lang parameter
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        final Locale locale = new Locale(lang);
        request = new HttpServletRequestWrapper(request) {
            @Override
            public Locale getLocale() {
                return locale;
            }
        };

        // Get the messages in the given language for the requested provider
        MessageFilter filter = new MessageFilter().lang(lang);
        Date now = "true".equals(activeNow) ? new Date() : null;
        Integer id = StringUtils.isNumeric(messageId) ? Integer.valueOf(messageId) : null;
        Set<Integer> areaHeadings = StringUtils.isNotBlank(areaHeadingIds) ? Arrays
                .asList(areaHeadingIds.split(",")).stream().map(Integer::valueOf).collect(Collectors.toSet())
                : null;

        List<Message> messages = providerServices.stream().flatMap(p -> p.getCachedMessages(filter).stream())
                // Filter on message id
                .filter(msg -> (id == null || id.equals(msg.getId())))
                // Filter on active messages
                .filter(msg -> (now == null || msg.getValidFrom() == null || msg.getValidFrom().before(now)))
                // Filter on area headings
                .filter(msg -> (areaHeadings == null || areaHeadings.contains(getAreaHeadingId(msg))))
                .collect(Collectors.toList());

        // Register the attributes to be used on the JSP apeg
        request.setAttribute("messages", messages);
        request.setAttribute("baseUri", app.getBaseUri());
        request.setAttribute("lang", lang);
        request.setAttribute("locale", locale);
        request.setAttribute("provider", providerId);

        if (request.getServletPath().endsWith("pdf")) {
            generatePdfFile(request, response);
        } else {
            generateHtmlPage(request, response);
        }
    }

    /**
     * Returns the area heading ID of the given message, or null if none exists
     * @param msg the message
     * @return the area heading ID of the given message
     */
    public Integer getAreaHeadingId(Message msg) {
        Area area = TldFunctions.getAreaHeading(msg);
        return (area != null) ? area.getId() : null;
    }

    /**
     * Generates a HTML page containing the MSI message details
     * @param request the HTTP servlet request
     * @param response the HTTP servlet response
     */
    private void generateHtmlPage(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        // Normal processing
        request.getRequestDispatcher(DETAILS_JSP_FILE).include(request, response);
        response.flushBuffer();
    }

    /**
     * Generates a PDF file containing the MSI message details
     * @param request the HTTP servlet request
     * @param response the HTTP servlet response
     */
    private void generatePdfFile(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        //Capture the content for this request
        ContentCaptureServletResponse capContent = new ContentCaptureServletResponse(response);
        request.getRequestDispatcher(DETAILS_JSP_FILE).include(request, capContent);

        // Check if there is content. Could be a redirect...
        if (!capContent.hasContent()) {
            return;
        }

        try {
            // Clean up the response HTML to a document that is readable by the XHTML renderer.
            String content = capContent.getContent();
            Document xhtmlContent = cleanHtml(content);

            long t0 = System.currentTimeMillis();
            String baseUri = app.getBaseUri();
            log.info("Generating PDF for " + baseUri);

            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(xhtmlContent, baseUri);
            renderer.layout();

            response.setContentType("application/pdf");
            if (StringUtils.isNotBlank(request.getParameter("attachment"))) {
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + request.getParameter("attachment"));
            }
            OutputStream browserStream = response.getOutputStream();
            renderer.createPDF(browserStream);

            log.info("Completed PDF generation in " + (System.currentTimeMillis() - t0) + " ms");
        } catch (DocumentException e) {
            throw new ServletException(e);
        }
    }

    /**
     * Use JTidy to clean up the HTML
     * @param html the HTML to clean up
     * @return the resulting XHTML
     */
    public Document cleanHtml(String html) {
        Tidy tidy = new Tidy();

        tidy.setShowWarnings(false); //to hide errors
        tidy.setQuiet(true); //to hide warning

        tidy.setXHTML(true);
        return tidy.parseDOM(new StringReader(html), new StringWriter());
    }

    /**
     * Response wrapper
     * Collects all contents
     */
    public static class ContentCaptureServletResponse extends HttpServletResponseWrapper {

        private StringWriter contentWriter;
        private PrintWriter writer;

        /**
         * Constructor
         * @param originalResponse the original response
         */
        public ContentCaptureServletResponse(HttpServletResponse originalResponse) {
            super(originalResponse);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            throw new IllegalStateException("Call getWriter()");
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public PrintWriter getWriter() throws IOException {
            if (writer == null) {
                contentWriter = new StringWriter();
                writer = new PrintWriter(contentWriter);
            }
            return writer;
        }

        /**
         * Returns if the response contains content
         * @return if the response contains content
         */
        public boolean hasContent() {
            return (writer != null);
        }

        /**
         * Returns the contents of the response as a string
         * @return the contents of the response as a string
         */
        public String getContent() {
            if (writer == null) {
                return "<html/>";
            }
            writer.flush();
            return contentWriter.toString();
        }
    }
}