org.mapfish.print.servlet.MapPrinterServlet Maven / Gradle / Ivy
/*
* Copyright (C) 2013 Camptocamp
*
* This file is part of MapFish Print
*
* MapFish Print 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.
*
* MapFish Print 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 MapFish Print. If not, see .
*/
package org.mapfish.print.servlet;
import com.google.common.io.CharStreams;
import com.google.common.io.Closer;
import org.apache.log4j.Logger;
import org.pvalsecc.misc.FileUtilities;
import org.mapfish.print.utils.PJsonObject;
import org.mapfish.print.output.OutputFormat;
import org.mapfish.print.MapPrinter;
import org.mapfish.print.Constants;
import org.json.JSONWriter;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.itextpdf.text.DocumentException;
/**
* Main print servlet.
*/
public class MapPrinterServlet extends BaseMapServlet {
public static final Logger SPEC_LOGGER = Logger.getLogger(BaseMapServlet.class.getPackage().toString() + ".spec");
private static final long serialVersionUID = -4706371598927161642L;
private static final String CONTEXT_TEMPDIR = "javax.servlet.context.tempdir";
private static final String INFO_URL = "/info.json";
private static final String PRINT_URL = "/print.pdf";
private static final String CREATE_URL = "/create.json";
protected static final String TEMP_FILE_PREFIX = "mapfish-print";
private static final String TEMP_FILE_SUFFIX = ".printout";
private String app = null;
private static final int TEMP_FILE_PURGE_SECONDS = 10 * 60;
private File tempDir = null;
private String encoding = null;
/**
* Tells if a thread is alread purging the old temporary files or not.
*/
private AtomicBoolean purging = new AtomicBoolean(false);
/**
* Map of temporary files.
*/
private final Map tempFiles = new HashMap();
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
//do the routing in function of the actual URL
String additionalPath = httpServletRequest.getPathInfo().trim();
if (additionalPath.isEmpty()) {
// handle an odd case where path info returns an empty string
additionalPath = httpServletRequest.getServletPath();
}
if (additionalPath.equals(PRINT_URL)) {
createAndGetPDF(httpServletRequest, httpServletResponse);
} else if (additionalPath.equals(INFO_URL)) {
getInfo(httpServletRequest, httpServletResponse, getBaseUrl(httpServletRequest));
} else if (additionalPath.startsWith("/") && additionalPath.endsWith(TEMP_FILE_SUFFIX)) {
getFile(httpServletRequest, httpServletResponse, additionalPath.substring(1, additionalPath.length() - TEMP_FILE_SUFFIX.length()));
} else {
error(httpServletResponse, "Unknown method: " + additionalPath, 404);
}
}
protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
final String additionalPath = httpServletRequest.getPathInfo();
if (additionalPath.equals(PRINT_URL)) {
createAndGetPDF(httpServletRequest, httpServletResponse);
} else if (additionalPath.equals(CREATE_URL)) {
createPDF(httpServletRequest, httpServletResponse, getBaseUrl(httpServletRequest));
} else {
error(httpServletResponse, "Unknown method: " + additionalPath, 404);
}
}
public void init() throws ServletException {
//get rid of the temporary files that were present before the servlet was started.
File dir = getTempDir();
File[] files = dir.listFiles();
for (File file : files) {
deleteFile(file);
}
}
public void destroy() {
synchronized (tempFiles) {
for (File file : tempFiles.values()) {
deleteFile(file);
}
tempFiles.clear();
}
super.destroy();
}
/**
* All in one method: create and returns the PDF to the client. Avoid to use
* it, the accents in the spec are not all supported.
*/
protected void createAndGetPDF(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
//get the spec from the query
TempFile tempFile = null;
String spec = null;
try {
httpServletRequest.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if (httpServletRequest.getMethod() == "POST") {
try {
spec = getSpecFromPostBody(httpServletRequest);
} catch (IOException e) {
error(httpServletResponse, "Missing 'spec' in request body", 500);
return;
}
} else {
spec = httpServletRequest.getParameter("spec");
}
if (spec == null) {
error(httpServletResponse, "Missing 'spec' parameter", 500);
return;
}
try {
tempFile = doCreatePDFFile(spec, httpServletRequest);
sendPdfFile(httpServletResponse, tempFile, Boolean.parseBoolean(httpServletRequest.getParameter("inline")));
} catch (Throwable e) {
error(httpServletResponse, e);
} finally {
deleteFile(tempFile);
}
}
/**
* Create the PDF and returns to the client (in JSON) the URL to get the PDF.
*/
protected void createPDF(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String basePath) throws ServletException {
TempFile tempFile = null;
try {
purgeOldTemporaryFiles();
String spec = getSpecFromPostBody(httpServletRequest);
tempFile = doCreatePDFFile(spec, httpServletRequest);
if (tempFile == null) {
error(httpServletResponse, "Missing 'spec' parameter", 500);
return;
}
} catch (Throwable e) {
deleteFile(tempFile);
error(httpServletResponse, e);
return;
}
final String id = generateId(tempFile);
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter writer = null;
try {
writer = httpServletResponse.getWriter();
JSONWriter json = new JSONWriter(writer);
json.object();
{
json.key("getURL").value(basePath + "/" + id + TEMP_FILE_SUFFIX);
}
json.endObject();
} catch (JSONException e) {
deleteFile(tempFile);
throw new ServletException(e);
} catch (IOException e) {
deleteFile(tempFile);
throw new ServletException(e);
} finally {
if(writer != null) {
writer.close();
}
}
addTempFile(tempFile, id);
}
protected void addTempFile(TempFile tempFile, String id) {
synchronized (tempFiles) {
tempFiles.put(id, tempFile);
}
}
protected String getSpecFromPostBody(HttpServletRequest httpServletRequest) throws IOException {
if(httpServletRequest.getParameter("spec") != null) {
return httpServletRequest.getParameter("spec");
}
Closer closer = Closer.create();
try {
final InputStreamReader reader = closer.register(new InputStreamReader(httpServletRequest.getInputStream(), getEncoding()));
BufferedReader bufferedReader = closer.register(new BufferedReader(reader));
final String spec = CharStreams.toString(bufferedReader);
return spec;
} finally {
closer.close();
}
}
/**
* Get and cache the used Encoding.
*/
protected String getEncoding() {
if (encoding == null) {
encoding = getInitParameter("encoding");
LOGGER.debug("Using '" + encoding + "' to encode Inputcontent.");
}
if (encoding == null) {
return "UTF-8";
} else {
return encoding;
}
}
/**
* To get the PDF created previously.
*/
protected void getFile(HttpServletRequest req, HttpServletResponse httpServletResponse, String id) throws IOException, ServletException {
final TempFile file;
synchronized (tempFiles) {
file = tempFiles.get(id);
}
if (file == null) {
error(httpServletResponse, "File with id=" + id + " unknown", 404);
return;
}
sendPdfFile(httpServletResponse, file, Boolean.parseBoolean(req.getParameter("inline")));
}
/**
* To get (in JSON) the information about the available formats and CO.
*/
protected void getInfo(HttpServletRequest req, HttpServletResponse resp, String basePath) throws ServletException, IOException {
app = req.getParameter("app");
//System.out.println("app = "+app);
MapPrinter printer = getMapPrinter(app);
try {
resp.setContentType("application/json; charset=utf-8");
final PrintWriter writer = resp.getWriter();
try {
final String var = req.getParameter("var");
if (var != null) {
writer.print(var + "=");
}
JSONWriter json = new JSONWriter(writer);
try {
json.object();
{
printer.printClientConfig(json);
String urlToUseInSpec = basePath;
String proxyUrl = printer.getConfig().getProxyBaseUrl();
if (proxyUrl != null) {
urlToUseInSpec = proxyUrl;
}
json.key("printURL").value(urlToUseInSpec + PRINT_URL);
json.key("createURL").value(urlToUseInSpec + CREATE_URL);
if (app != null) {
json.key("app").value(app);
}
}
json.endObject();
} catch (JSONException e) {
throw new ServletException(e);
}
if (var != null) {
writer.print(";");
}
} finally {
writer.close();
}
} finally {
if (printer != null) {
printer.stop();
}
}
}
/**
* Do the actual work of creating the PDF temporary file.
* @throws InterruptedException
*/
protected TempFile doCreatePDFFile(String spec, HttpServletRequest httpServletRequest) throws IOException, DocumentException, ServletException, InterruptedException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Generating PDF for spec=" + spec);
}
if (SPEC_LOGGER.isInfoEnabled()) {
SPEC_LOGGER.info(spec);
}
PJsonObject specJson = MapPrinter.parseSpec(spec);
if (specJson.has("app")) {
app = specJson.getString("app");
} else {
app = null;
}
MapPrinter mapPrinter = getMapPrinter(app);
try {
Map headers = new HashMap();
TreeSet configHeaders = mapPrinter.getConfig().getHeaders();
if (configHeaders == null) {
configHeaders = new TreeSet();
configHeaders.add("Referer");
configHeaders.add("Cookie");
}
for (Iterator header_iter = configHeaders.iterator(); header_iter.hasNext();) {
String header = header_iter.next();
if (httpServletRequest.getHeader(header) != null) {
headers.put(header, httpServletRequest.getHeader(header));
}
}
final OutputFormat outputFormat = mapPrinter.getOutputFormat(specJson);
// create a temporary file that will contain the PDF
final File tempJavaFile = File.createTempFile(TEMP_FILE_PREFIX,
"." + outputFormat.getFileSuffix() + TEMP_FILE_SUFFIX, getTempDir());
TempFile tempFile = new TempFile(tempJavaFile, specJson, outputFormat);
FileOutputStream out = null;
try {
out = new FileOutputStream(tempFile);
mapPrinter.print(specJson, out, headers);
return tempFile;
} catch (IOException e) {
deleteFile(tempFile);
throw e;
} catch (DocumentException e) {
deleteFile(tempFile);
throw e;
} catch (InterruptedException e) {
deleteFile(tempFile);
throw e;
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (mapPrinter != null) {
mapPrinter.stop();
}
}
}
/**
* copy the PDF into the output stream
*/
protected void sendPdfFile(HttpServletResponse httpServletResponse, TempFile tempFile, boolean inline) throws IOException, ServletException {
FileInputStream pdf = new FileInputStream(tempFile);
final OutputStream response = httpServletResponse.getOutputStream();
MapPrinter mapPrinter = getMapPrinter(app);
try {
httpServletResponse.setContentType(tempFile.contentType());
if (!inline) {
final String fileName = tempFile.getOutputFileName(mapPrinter);
httpServletResponse.setHeader("Content-disposition", "attachment; filename=" + fileName);
}
FileUtilities.copyStream(pdf, response);
} finally {
if (mapPrinter != null) {
mapPrinter.stop();
}
try {
pdf.close();
} finally {
response.close();
}
}
}
/**
* Send an error XXX to the client with an exception
*/
protected void error(HttpServletResponse httpServletResponse, Throwable e) {
PrintWriter out = null;
try {
httpServletResponse.setContentType("text/plain");
httpServletResponse.setStatus(500);
out = httpServletResponse.getWriter();
out.println("Error while generating PDF:");
e.printStackTrace(out);
LOGGER.error("Error while generating PDF", e);
} catch (IOException ex) {
throw new RuntimeException(e);
} finally {
if (out != null) {
out.close();
}
}
}
/**
* Send an error XXX to the client with a message
*/
protected void error(HttpServletResponse httpServletResponse, String message, int code) {
PrintWriter out = null;
try {
httpServletResponse.setContentType("text/plain");
httpServletResponse.setStatus(code);
out = httpServletResponse.getWriter();
out.println("Error while generating PDF:");
out.println(message);
LOGGER.error("Error while generating PDF: " + message);
} catch (IOException ex) {
throw new RuntimeException(ex);
} finally {
if (out != null) {
out.close();
}
}
}
/**
* Get and cache the temporary directory to use for saving the generated PDF files.
*/
protected File getTempDir() {
if (tempDir == null) {
String tempDirPath = getInitParameter("tempdir");
if (tempDirPath != null) {
tempDir = new File(tempDirPath);
} else {
tempDir = (File) getServletContext().getAttribute(CONTEXT_TEMPDIR);
}
if (!tempDir.exists() && !tempDir.mkdirs()) {
throw new RuntimeException("unable to create dir:" + tempDir);
}
}
LOGGER.debug("Using '" + tempDir.getAbsolutePath() + "' as temporary directory");
return tempDir;
}
/**
* If the file is defined, delete it.
*/
protected void deleteFile(File file) {
if (file != null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Deleting PDF file: " + file.getName());
}
if (!file.delete()) {
LOGGER.warn("Cannot delete file:" + file.getAbsolutePath());
}
}
}
/**
* Get the ID to use in function of the filename (filename without the prefix and the extension).
*/
protected String generateId(File tempFile) {
final String name = tempFile.getName();
return name.substring(
TEMP_FILE_PREFIX.length(),
name.length() - TEMP_FILE_SUFFIX.length());
}
protected String getBaseUrl(HttpServletRequest httpServletRequest) {
final String additionalPath = httpServletRequest.getPathInfo();
String fullUrl = httpServletRequest.getParameter("url");
if (fullUrl != null) {
return fullUrl.replaceFirst(additionalPath + "$", "");
} else {
return httpServletRequest.getRequestURL().toString().replaceFirst(additionalPath + "$", "");
}
}
/**
* Will purge all the known temporary files older than TEMP_FILE_PURGE_SECONDS.
*/
protected void purgeOldTemporaryFiles() {
if (!purging.getAndSet(true)) {
final long minTime = System.currentTimeMillis() - TEMP_FILE_PURGE_SECONDS * 1000L;
synchronized (tempFiles) {
Iterator> it = tempFiles.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue().creationTime < minTime) {
deleteFile(entry.getValue());
it.remove();
}
}
}
purging.set(false);
}
}
static class TempFile extends File {
private static final long serialVersionUID = 455104129549002361L;
private final long creationTime;
public final String printedLayoutName;
public final String outputFileName;
private final String contentType;
private String suffix;
public TempFile(File tempFile, PJsonObject jsonSpec, OutputFormat format) {
super(tempFile.getAbsolutePath());
creationTime = System.currentTimeMillis();
this.outputFileName = jsonSpec.optString(Constants.OUTPUT_FILENAME_KEY);
this.printedLayoutName = jsonSpec.optString(Constants.JSON_LAYOUT_KEY, null);
this.suffix = format.getFileSuffix();
this.contentType = format.getContentType();
}
public String getOutputFileName(MapPrinter mapPrinter) {
if(outputFileName != null) {
return formatFileName(suffix, outputFileName, new Date());
} else {
return formatFileName(suffix, mapPrinter.getOutputFilename(printedLayoutName, getName()), new Date());
}
}
public static String formatFileName(String suffix, String startingName, Date date) {
Matcher matcher = Pattern.compile("\\$\\{(.+?)\\}").matcher(startingName);
HashMap replacements = new HashMap();
while(matcher.find()) {
String pattern = matcher.group(1);
String key = "${"+pattern+"}";
replacements.put(key, findReplacement(pattern, date));
}
String result = startingName;
for(Map.Entry entry: replacements.entrySet()) {
result = result.replace(entry.getKey(), entry.getValue());
}
while(suffix.startsWith(".")) {
suffix = suffix.substring(1);
}
if(suffix.isEmpty() || result.toLowerCase().endsWith("."+suffix.toLowerCase())) {
return result;
} else {
return result+"."+suffix;
}
}
public static String cleanUpName(String original) {
return original.replace(",","").replaceAll("\\s+", "_");
}
private static String findReplacement(String pattern, Date date) {
if (pattern.toLowerCase().equals("date")) {
return cleanUpName(DateFormat.getDateInstance().format(date));
} else if (pattern.toLowerCase().equals("datetime")) {
return cleanUpName(DateFormat.getDateTimeInstance().format(date));
} else if (pattern.toLowerCase().equals("time")) {
return cleanUpName(DateFormat.getTimeInstance().format(date));
} else {
try {
return new SimpleDateFormat(pattern).format(date);
} catch (Exception e) {
LOGGER.error("Unable to format timestamp according to pattern: "+pattern, e);
return "${"+pattern+"}";
}
}
}
public String contentType() {
return contentType;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy