org.structr.pdf.servlet.PdfServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.structr.pdf.servlet.PdfServlet.java

Source

/**
 * Copyright (C) 2010-2018 Structr GmbH
 *
 * This file is part of Structr <http://structr.org>.
 *
 * Structr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Structr 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Structr.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.structr.pdf.servlet;

import com.github.jhonnymertz.wkhtmltopdf.wrapper.Pdf;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.params.Param;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jetty.io.EofException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.api.config.Settings;
import org.structr.common.ThreadLocalMatcher;
import org.structr.common.error.FrameworkException;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.graph.Tx;
import org.structr.rest.service.StructrHttpServiceConfig;
import org.structr.web.common.RenderContext;
import org.structr.web.common.StringRenderBuffer;
import org.structr.web.entity.dom.DOMNode;
import org.structr.web.servlet.HtmlServlet;
import org.structr.websocket.command.AbstractCommand;

import javax.servlet.AsyncContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;

public class PdfServlet extends HtmlServlet {

    private static final Logger logger = LoggerFactory.getLogger(HtmlServlet.class.getName());

    private static final List<String> customResponseHeaders = new LinkedList<>();
    private static final ThreadLocalMatcher threadLocalUUIDMatcher = new ThreadLocalMatcher("[a-fA-F0-9]{32}");
    private static final ExecutorService threadPool = Executors.newCachedThreadPool();

    private final Pattern FilenameCleanerPattern = Pattern.compile("[\n\r]",
            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    private final StructrHttpServiceConfig config = new StructrHttpServiceConfig();
    private final Set<String> possiblePropertyNamesForEntityResolving = new LinkedHashSet<>();

    private boolean isAsync = false;

    public PdfServlet() {

        final String customResponseHeadersString = Settings.HtmlCustomResponseHeaders.getValue();
        if (StringUtils.isNotBlank(customResponseHeadersString)) {

            customResponseHeaders.addAll(Arrays.asList(customResponseHeadersString.split("[ ,]+")));
        }

        // resolving properties
        final String resolvePropertiesSource = Settings.HtmlResolveProperties.getValue();
        for (final String src : resolvePropertiesSource.split("[, ]+")) {

            final String name = src.trim();
            if (StringUtils.isNotBlank(name)) {

                possiblePropertyNamesForEntityResolving.add(name);
            }
        }

        this.isAsync = Settings.Async.getValue();
    }

    @Override
    public StructrHttpServiceConfig getConfig() {
        return config;
    }

    @Override
    public String getModuleName() {
        return "pdf";
    }

    @Override
    public void init() {

        try (final Tx tx = StructrApp.getInstance().tx()) {

            AbstractCommand.getOrCreateHiddenDocument();
            tx.success();

        } catch (FrameworkException fex) {
            logger.warn("Unable to create shadow page: {}", fex.getMessage());
        }
    }

    @Override
    public void destroy() {
    }

    @Override
    protected void renderAsyncOutput(HttpServletRequest request, HttpServletResponse response, App app,
            RenderContext renderContext, DOMNode rootElement) throws IOException {
        final AsyncContext async = request.startAsync();
        final ServletOutputStream out = async.getResponse().getOutputStream();
        final AtomicBoolean finished = new AtomicBoolean(false);
        final DOMNode rootNode = rootElement;

        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment;filename=\"FileName.pdf\"");

        threadPool.submit(new Runnable() {

            @Override
            public void run() {

                try (final Tx tx = app.tx()) {

                    // render
                    rootNode.render(renderContext, 0);
                    finished.set(true);

                    tx.success();

                } catch (Throwable t) {

                    t.printStackTrace();
                    logger.warn("Error while rendering page {}: {}", rootNode.getName(), t.getMessage());

                    try {

                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                        finished.set(true);

                    } catch (IOException ex) {
                        logger.warn("", ex);
                    }
                }
            }

        });

        // start output write listener
        out.setWriteListener(new WriteListener() {

            @Override
            public void onWritePossible() throws IOException {

                try {

                    final Queue<String> queue = renderContext.getBuffer().getQueue();
                    String pageContent = "";
                    while (out.isReady()) {

                        String buffer = null;

                        synchronized (queue) {
                            buffer = queue.poll();
                        }

                        if (buffer != null) {

                            pageContent += buffer;

                        } else {

                            if (finished.get()) {

                                // TODO: implement parameters for wkhtmltopdf in settings

                                Pdf pdf = new Pdf();
                                pdf.addPageFromString(pageContent);

                                out.write(pdf.getPDF());

                                async.complete();

                                // prevent this block from being called again
                                break;
                            }

                            Thread.sleep(1);
                        }
                    }

                } catch (EofException ee) {
                    logger.warn(
                            "Could not flush the response body content to the client, probably because the network connection was terminated.");
                } catch (IOException | InterruptedException t) {
                    logger.warn("Unexpected exception", t);
                }
            }

            @Override
            public void onError(Throwable t) {
                if (t instanceof EofException) {
                    logger.warn(
                            "Could not flush the response body content to the client, probably because the network connection was terminated.");
                } else {
                    logger.warn("Unexpected exception", t);
                }
            }
        });
    }

    @Override
    protected void writeOutputSteam(HttpServletResponse response, StringRenderBuffer buffer) throws IOException {
        logger.warn("This is awesome!!!");
        response.getOutputStream().write(buffer.getBuffer().toString().getBytes("utf-8"));
        response.getOutputStream().flush();
        response.getOutputStream().close();
    }

}