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

org.mapfish.print.servlet.MapPrinterServlet Maven / Gradle / Ivy

package org.mapfish.print.servlet;

import net.sf.jasperreports.engine.fonts.FontFamily;
import net.sf.jasperreports.extensions.ExtensionsEnvironment;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.jfree.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import org.mapfish.print.Constants;
import org.mapfish.print.ExceptionUtils;
import org.mapfish.print.MapPrinter;
import org.mapfish.print.MapPrinterFactory;
import org.mapfish.print.config.Configuration;
import org.mapfish.print.config.Template;
import org.mapfish.print.processor.Processor;
import org.mapfish.print.processor.http.matcher.UriMatchers;
import org.mapfish.print.servlet.job.JobManager;
import org.mapfish.print.servlet.job.NoSuchReferenceException;
import org.mapfish.print.servlet.job.PrintJobStatus;
import org.mapfish.print.servlet.job.impl.PrintJobEntryImpl;
import org.mapfish.print.servlet.job.loader.ReportLoader;
import org.mapfish.print.url.data.Handler;
import org.mapfish.print.wrapper.json.PJsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
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 static org.mapfish.print.servlet.ServletMapPrinterFactory.DEFAULT_CONFIGURATION_FILE_KEY;

/**
 * The default servlet.
 */
@Controller
public class MapPrinterServlet extends BaseMapServlet {

    /**
     * The url path for capabilities requests.
     */
    public static final String CAPABILITIES_URL = "/capabilities.json";
    /**
     * The url path to list all registered configurations.
     */
    public static final String LIST_APPS_URL = "/apps.json";
    /**
     * The url path to get a sample print request.
     */
    public static final String EXAMPLE_REQUEST_URL = "/exampleRequest.json";
    /**
     * The url path to create and get a report.
     */
    public static final String CREATE_AND_GET_URL = "/buildreport";
    /**
     * The url path to get the status for a print task.
     */
    public static final String STATUS_URL = "/status";
    /**
     * The url path to cancel a print task.
     */
    public static final String CANCEL_URL = "/cancel";
    /**
     * The url path to create a print task and to get a finished print.
     */
    public static final String REPORT_URL = "/report";
    /**
     * The url path to get the list of fonts available to geotools.
     */
    public static final String FONTS_URL = "/fonts";
    /**
     * The key containing an error message for failed jobs.
     */
    public static final String JSON_ERROR = "error";
    /**
     * The application ID which indicates the configuration file to load.
     */
    public static final String JSON_APP = "app";

    /* Registry keys */
    /**
     * If the job is done (value is true) or not (value is false).
     * 

* Part of the {@link #getStatus(String, String, javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse)} response. */ public static final String JSON_DONE = "done"; /** * The status of the job. One of the following values: *
    *
  • waiting
  • *
  • running
  • *
  • finished
  • *
  • cancelled
  • *
  • error
  • *
* Part of the {@link #getStatus(String, String, javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse)} response */ public static final String JSON_STATUS = "status"; /** * The elapsed time in ms from the point the job started. If the job is finished, this is the duration it * took to process the job. *

* Part of the {@link #getStatus(String, String, javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse)} response. */ public static final String JSON_ELAPSED_TIME = "elapsedTime"; /** * A rough estimate for the time in ms the job still has to wait in the queue until it starts processing. *

* Part of the {@link #getStatus(String, String, javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse)} response. */ public static final String JSON_WAITING_TIME = "waitingTime"; /** * The key containing the print job reference ID in the create report response. */ public static final String JSON_PRINT_JOB_REF = "ref"; /** * The json key in the create report response containing a link to get the status of the print job. */ public static final String JSON_STATUS_LINK = "statusURL"; /** * The json key in the create report and status responses containing a link to download the report. */ public static final String JSON_DOWNLOAD_LINK = "downloadURL"; /** * The JSON key in the request spec that contains the outputFormat. This value will be put into the spec * by the servlet. there is not need for the post to do this. */ public static final String JSON_OUTPUT_FORMAT = "outputFormat"; /** * The json tag referring to the attributes. */ public static final String JSON_ATTRIBUTES = "attributes"; /** * The json property to add the request headers from the print request. *

* The request headers from the print request are needed by certain processors, the headers are added to * the request JSON data for those processors. */ public static final String JSON_REQUEST_HEADERS = "requestHeaders"; private static final Logger LOGGER = LoggerFactory.getLogger(MapPrinterServlet.class); private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{(\\S+)}"); private static final int JSON_INDENT_FACTOR = 4; private static final List REQUEST_ID_HEADERS = Arrays.asList( "X-Request-ID", "X-Correlation-ID", "Request-ID", "X-Varnish", "X-Amzn-Trace-Id" ); static { Handler.configureProtocolHandler(); } private final JobManager jobManager; private final List reportLoaders; private final MapPrinterFactory printerFactory; private final ApplicationContext context; private final ServletInfo servletInfo; private final MapPrinterFactory mapPrinterFactory; private long maxCreateAndGetWaitTimeInSeconds; @Autowired public MapPrinterServlet( final JobManager jobManager, final List reportLoaders, final MapPrinterFactory printerFactory, final ApplicationContext context, final ServletInfo servletInfo, final MapPrinterFactory mapPrinterFactory) { this.jobManager = jobManager; this.reportLoaders = reportLoaders; this.printerFactory = printerFactory; this.context = context; this.servletInfo = servletInfo; this.mapPrinterFactory = mapPrinterFactory; } /** * Parse the print request json data. * * @param requestDataRaw the request json in string form * @param httpServletResponse the response object to use for returning errors if needed */ private static PJsonObject parseJson( final String requestDataRaw, final HttpServletResponse httpServletResponse) { try { if (requestDataRaw == null) { error(httpServletResponse, "Missing post data. The post payload must either be a form post with a spec " + "parameter or " + "must be a raw json post with the request.", HttpStatus.INTERNAL_SERVER_ERROR); return null; } String requestData = requestDataRaw; if (!requestData.startsWith("spec=") && !requestData.startsWith("{")) { try { requestData = URLDecoder.decode(requestData, Constants.DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw ExceptionUtils.getRuntimeException(e); } } if (requestData.startsWith("spec=")) { requestData = requestData.substring("spec=".length()); } try { return MapPrinter.parseSpec(requestData); } catch (RuntimeException e) { try { return MapPrinter.parseSpec(URLDecoder.decode(requestData, Constants.DEFAULT_ENCODING)); } catch (UnsupportedEncodingException uee) { throw ExceptionUtils.getRuntimeException(e); } } } catch (RuntimeException e) { LOGGER.warn("Error parsing request data: {}", requestDataRaw); throw e; } } /** * If the request contains a header that specifies a request ID, add it to the ref. The ref shows up in * every logs, that way, we can trace the request ID across applications. */ private static String maybeAddRequestId(final String ref, final HttpServletRequest request) { final Optional headerName = REQUEST_ID_HEADERS.stream().filter(h -> request.getHeader(h) != null).findFirst(); return headerName.map(s -> ref + "@" + request.getHeader(s).replaceAll("[^a-zA-Z0-9._:-]", "_") ).orElse(ref); } /** * Get a status report on a job. Returns the following json: *

*

     *  {"time":0,"count":0,"done":false}
     * 
* * @param appId the app ID * @param referenceId the job reference * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param statusRequest the request object * @param statusResponse the response object */ @RequestMapping(value = "/{appId}" + STATUS_URL + "/{referenceId:\\S+}.json", method = RequestMethod.GET) public final void getStatusSpecificAppId( @SuppressWarnings("unused") @PathVariable final String appId, @PathVariable final String referenceId, @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest statusRequest, final HttpServletResponse statusResponse) { getStatus(referenceId, jsonpCallback, statusRequest, statusResponse); } /** * Get a status report on a job. Returns the following json: *

*

     *  {"time":0,"count":0,"done":false}
     * 
* * @param referenceId the job reference * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param statusRequest the request object * @param statusResponse the response object */ @RequestMapping(value = STATUS_URL + "/{referenceId:\\S+}.json", method = RequestMethod.GET) public final void getStatus( @PathVariable final String referenceId, @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest statusRequest, final HttpServletResponse statusResponse) { MDC.put(Processor.MDC_JOB_ID_KEY, referenceId); setNoCache(statusResponse); try { PrintJobStatus status = this.jobManager.getStatus(referenceId); setContentType(statusResponse, jsonpCallback); try (PrintWriter writer = statusResponse.getWriter()) { appendJsonpCallback(jsonpCallback, writer); JSONWriter json = new JSONWriter(writer); json.object(); { json.key(JSON_DONE).value(status.isDone()); json.key(JSON_STATUS).value(status.getStatus().toString().toLowerCase()); json.key(JSON_ELAPSED_TIME).value(status.getElapsedTime()); json.key(JSON_WAITING_TIME).value(status.getWaitingTime()); if (!StringUtils.isEmpty(status.getError())) { json.key(JSON_ERROR).value(status.getError()); } addDownloadLinkToJson(statusRequest, referenceId, json); } json.endObject(); appendJsonpCallbackEnd(jsonpCallback, writer); } } catch (JSONException | IOException e) { throw ExceptionUtils.getRuntimeException(e); } catch (NoSuchReferenceException e) { error(statusResponse, e.getMessage(), HttpStatus.NOT_FOUND); } } /** * Cancel a job. *

* Even if a job was already finished, subsequent status requests will return that the job was canceled. * * @param appId the app ID * @param referenceId the job reference * @param statusResponse the response object */ @RequestMapping(value = "/{appId}" + CANCEL_URL + "/{referenceId:\\S+}", method = RequestMethod.DELETE) public final void cancelSpecificAppId( @SuppressWarnings("unused") @PathVariable final String appId, @PathVariable final String referenceId, final HttpServletResponse statusResponse) { cancel(referenceId, statusResponse); } /** * Cancel a job. *

* Even if a job was already finished, subsequent status requests will return that the job was canceled. * * @param referenceId the job reference * @param statusResponse the response object */ @RequestMapping(value = CANCEL_URL + "/{referenceId:\\S+}", method = RequestMethod.DELETE) public final void cancel( @PathVariable final String referenceId, final HttpServletResponse statusResponse) { MDC.put(Processor.MDC_JOB_ID_KEY, referenceId); setNoCache(statusResponse); try { this.jobManager.cancel(referenceId); } catch (NoSuchReferenceException e) { error(statusResponse, e.getMessage(), HttpStatus.NOT_FOUND); } } /** * Add the print job to the job queue. * * @param appId the id of the app to get the request for. * @param format the format of the returned report * @param requestData a json formatted string with the request data required to perform the report * generation. * @param createReportRequest the request object * @param createReportResponse the response object */ @RequestMapping(value = "/{appId}" + REPORT_URL + ".{format:\\w+}", method = RequestMethod.POST) public final void createReport( @PathVariable final String appId, @PathVariable final String format, @RequestBody final String requestData, final HttpServletRequest createReportRequest, final HttpServletResponse createReportResponse) throws NoSuchAppException { setNoCache(createReportResponse); String ref = createAndSubmitPrintJob(appId, format, requestData, createReportRequest, createReportResponse); if (ref == null) { error(createReportResponse, "Failed to create a print job", HttpStatus.INTERNAL_SERVER_ERROR); return; } createReportResponse.setContentType("application/json; charset=utf-8"); try (PrintWriter writer = createReportResponse.getWriter()) { JSONWriter json = new JSONWriter(writer); json.object(); { json.key(JSON_PRINT_JOB_REF).value(ref); String statusURL = getBaseUrl(createReportRequest) + STATUS_URL + "/" + ref + ".json"; json.key(JSON_STATUS_LINK).value(statusURL); addDownloadLinkToJson(createReportRequest, ref, json); } json.endObject(); } catch (JSONException | IOException e) { LOGGER.warn("Error generating the JSON response", e); } } /** * To get the PDF created previously. * * @param appId the app ID * @param referenceId the path to the file. * @param inline whether or not to inline the * @param getReportResponse the response object */ @RequestMapping(value = "/{appId}" + REPORT_URL + "/{referenceId:\\S+}", method = RequestMethod.GET) public final void getReportSpecificAppId( @SuppressWarnings("unused") @PathVariable final String appId, @PathVariable final String referenceId, @RequestParam(value = "inline", defaultValue = "false") final boolean inline, final HttpServletResponse getReportResponse) throws IOException, ServletException { getReport(referenceId, inline, getReportResponse); } /** * To get the PDF created previously. * * @param referenceId the path to the file. * @param inline whether or not to inline the * @param getReportResponse the response object */ @RequestMapping(value = REPORT_URL + "/{referenceId:\\S+}", method = RequestMethod.GET) public final void getReport( @PathVariable final String referenceId, @RequestParam(value = "inline", defaultValue = "false") final boolean inline, final HttpServletResponse getReportResponse) throws IOException, ServletException { MDC.put(Processor.MDC_JOB_ID_KEY, referenceId); setNoCache(getReportResponse); loadReport(referenceId, getReportResponse, new HandleReportLoadResult() { @Override public Void unknownReference( final HttpServletResponse httpServletResponse, final String referenceId) { error(httpServletResponse, "Error getting print with ref=" + referenceId + ": unknown reference", HttpStatus.NOT_FOUND); return null; } @Override public Void unsupportedLoader( final HttpServletResponse httpServletResponse, final String referenceId) { error(httpServletResponse, "Error getting print with ref=" + referenceId + " can not be loaded", HttpStatus.NOT_FOUND); return null; } @Override public Void successfulPrint( final PrintJobStatus successfulPrintResult, final HttpServletResponse httpServletResponse, final URI reportURI, final ReportLoader loader) throws IOException { sendReportFile(successfulPrintResult, httpServletResponse, loader, reportURI, inline); return null; } @Override public Void failedPrint( final PrintJobStatus failedPrintJob, final HttpServletResponse httpServletResponse) { error(httpServletResponse, failedPrintJob.getError(), HttpStatus.INTERNAL_SERVER_ERROR); return null; } @Override public Void printJobPending( final HttpServletResponse httpServletResponse, final String referenceId) { error(httpServletResponse, "Report has not yet completed processing", HttpStatus.ACCEPTED); return null; } }); } /** * Add the print job to the job queue. * * @param format the format of the returned report * @param requestData a json formatted string with the request data required to perform the report * generation. * @param createReportRequest the request object * @param createReportResponse the response object */ @RequestMapping(value = REPORT_URL + ".{format:\\w+}", method = RequestMethod.POST) public final void createReport( @PathVariable final String format, @RequestBody final String requestData, final HttpServletRequest createReportRequest, final HttpServletResponse createReportResponse) throws NoSuchAppException { setNoCache(createReportResponse); PJsonObject spec = parseJson(requestData, createReportResponse); if (spec == null) { return; } final String appId = spec.optString(JSON_APP, DEFAULT_CONFIGURATION_FILE_KEY); createReport(appId, format, requestData, createReportRequest, createReportResponse); } /** * add the print job to the job queue. * * @param appId the id of the app to get the request for. * @param format the format of the returned report * @param requestData a json formatted string with the request data required to perform the report * generation. * @param inline whether or not to inline the content * @param createReportRequest the request object * @param createReportResponse the response object */ @RequestMapping(value = "/{appId}" + CREATE_AND_GET_URL + ".{format:\\w+}", method = RequestMethod.POST) public final void createReportAndGet( @PathVariable final String appId, @PathVariable final String format, @RequestBody final String requestData, @RequestParam(value = "inline", defaultValue = "false") final boolean inline, final HttpServletRequest createReportRequest, final HttpServletResponse createReportResponse) throws IOException, ServletException, InterruptedException, NoSuchAppException { setNoCache(createReportResponse); String ref = createAndSubmitPrintJob(appId, format, requestData, createReportRequest, createReportResponse); if (ref == null) { error(createReportResponse, "Failed to create a print job", HttpStatus.INTERNAL_SERVER_ERROR); return; } final HandleReportLoadResult handler = new HandleReportLoadResult() { @Override public Boolean unknownReference( final HttpServletResponse httpServletResponse, final String referenceId) { error(httpServletResponse, "Print with ref=" + referenceId + " unknown", HttpStatus.NOT_FOUND); return true; } @Override public Boolean unsupportedLoader( final HttpServletResponse httpServletResponse, final String referenceId) { error(httpServletResponse, "Print with ref=" + referenceId + " can not be loaded", HttpStatus.NOT_FOUND); return true; } @Override public Boolean successfulPrint( final PrintJobStatus successfulPrintResult, final HttpServletResponse httpServletResponse, final URI reportURI, final ReportLoader loader) throws IOException { sendReportFile(successfulPrintResult, httpServletResponse, loader, reportURI, inline); return true; } @Override public Boolean failedPrint( final PrintJobStatus failedPrintJob, final HttpServletResponse httpServletResponse) { error(httpServletResponse, failedPrintJob.getError(), HttpStatus.INTERNAL_SERVER_ERROR); return true; } @Override public Boolean printJobPending( final HttpServletResponse httpServletResponse, final String referenceId) { return false; } }; boolean isDone = false; long startWaitTime = System.currentTimeMillis(); final long maxWaitTimeInMillis = TimeUnit.SECONDS.toMillis(this.maxCreateAndGetWaitTimeInSeconds); while (!isDone && System.currentTimeMillis() - startWaitTime < maxWaitTimeInMillis) { Thread.sleep(TimeUnit.SECONDS.toMillis(1)); isDone = loadReport(ref, createReportResponse, handler); } } /** * add the print job to the job queue. * * @param format the format of the returned report * @param requestData a json formatted string with the request data required to perform the report * generation. * @param inline whether or not to inline the content * @param createReportRequest the request object * @param createReportResponse the response object */ @RequestMapping(value = CREATE_AND_GET_URL + ".{format:\\w+}", method = RequestMethod.POST) public final void createReportAndGetNoAppId( @PathVariable final String format, @RequestBody final String requestData, @RequestParam(value = "inline", defaultValue = "false") final boolean inline, final HttpServletRequest createReportRequest, final HttpServletResponse createReportResponse) throws IOException, ServletException, InterruptedException, NoSuchAppException { setNoCache(createReportResponse); PJsonObject spec = parseJson(requestData, createReportResponse); if (spec == null) { return; } String appId = spec.optString(JSON_APP, DEFAULT_CONFIGURATION_FILE_KEY); createReportAndGet(appId, format, requestData, inline, createReportRequest, createReportResponse); } /** * To get (in JSON) the information about the available formats and CO. * * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param listAppsResponse the response object */ @RequestMapping(value = LIST_APPS_URL, method = RequestMethod.GET) public final void listAppIds( @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletResponse listAppsResponse) throws ServletException, IOException { MDC.remove(Processor.MDC_JOB_ID_KEY); setCache(listAppsResponse); Set appIds = this.printerFactory.getAppIds(); setContentType(listAppsResponse, jsonpCallback); try (PrintWriter writer = listAppsResponse.getWriter()) { appendJsonpCallback(jsonpCallback, writer); JSONWriter json = new JSONWriter(writer); try { json.array(); for (String appId: appIds) { json.value(appId); } json.endArray(); } catch (JSONException e) { throw new ServletException(e); } appendJsonpCallbackEnd(jsonpCallback, writer); } } /** * To get (in JSON) the information about the available formats and CO. * * @param pretty if true then pretty print the capabilities * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param request the request * @param capabilitiesResponse the response object */ @RequestMapping(value = CAPABILITIES_URL, method = RequestMethod.GET) public final void getCapabilities( @RequestParam(value = "pretty", defaultValue = "false") final boolean pretty, @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest request, final HttpServletResponse capabilitiesResponse) throws ServletException, IOException { getCapabilities(DEFAULT_CONFIGURATION_FILE_KEY, pretty, jsonpCallback, request, capabilitiesResponse); } /** * To get (in JSON) the information about the available formats and CO. * * @param appId the name of the "app" or in other words, a mapping to the configuration file for * this request. * @param pretty if true then pretty print the capabilities * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param request the request * @param capabilitiesResponse the response object */ @RequestMapping(value = "/{appId}" + CAPABILITIES_URL, method = RequestMethod.GET) public final void getCapabilities( @PathVariable final String appId, @RequestParam(value = "pretty", defaultValue = "false") final boolean pretty, @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest request, final HttpServletResponse capabilitiesResponse) throws ServletException, IOException { MDC.remove(Processor.MDC_JOB_ID_KEY); setCache(capabilitiesResponse); MapPrinter printer; try { printer = this.printerFactory.create(appId); if (!checkReferer(request, printer)) { error(capabilitiesResponse, "Invalid referer", HttpStatus.FORBIDDEN); return; } } catch (NoSuchAppException e) { error(capabilitiesResponse, e.getMessage(), HttpStatus.NOT_FOUND); return; } setContentType(capabilitiesResponse, jsonpCallback); final ByteArrayOutputStream prettyPrintBuffer = new ByteArrayOutputStream(); try (Writer writer = pretty ? new OutputStreamWriter(prettyPrintBuffer, Constants.DEFAULT_CHARSET) : capabilitiesResponse.getWriter()) { if (!pretty && !StringUtils.isEmpty(jsonpCallback)) { writer.append(jsonpCallback).append("("); } JSONWriter json = new JSONWriter(writer); try { json.object(); { json.key(JSON_APP).value(appId); printer.printClientConfig(json); } { json.key("formats"); Set formats = printer.getOutputFormatsNames(); json.array(); for (String format: formats) { json.value(format); } json.endArray(); } json.endObject(); } catch (JSONException e) { throw new ServletException(e); } if (!pretty && !StringUtils.isEmpty(jsonpCallback)) { writer.append(");"); } } if (pretty) { final JSONObject jsonObject = new JSONObject(new String(prettyPrintBuffer.toByteArray(), Constants.DEFAULT_CHARSET)); if (!StringUtils.isEmpty(jsonpCallback)) { capabilitiesResponse.getOutputStream().print(jsonpCallback + "("); } capabilitiesResponse.getOutputStream().print(jsonObject.toString(JSON_INDENT_FACTOR)); if (!StringUtils.isEmpty(jsonpCallback)) { capabilitiesResponse.getOutputStream().print(");"); } } } /** * Get a sample request for the app. An empty response may be returned if there is not example request. * * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param request the request object * @param getExampleResponse the response object */ @RequestMapping(value = EXAMPLE_REQUEST_URL, method = RequestMethod.GET) public final void getExampleRequest( @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest request, final HttpServletResponse getExampleResponse) throws IOException { getExampleRequest(DEFAULT_CONFIGURATION_FILE_KEY, jsonpCallback, request, getExampleResponse); } /** * Get a sample request for the app. An empty response may be returned if there is not example request. * * @param appId the id of the app to get the request for. * @param jsonpCallback if given the result is returned with a function call wrapped around it * @param request the request object * @param getExampleResponse the response object */ @RequestMapping(value = "{appId}" + EXAMPLE_REQUEST_URL, method = RequestMethod.GET) public final void getExampleRequest( @PathVariable final String appId, @RequestParam(value = "jsonp", defaultValue = "") final String jsonpCallback, final HttpServletRequest request, final HttpServletResponse getExampleResponse) throws IOException { MDC.remove(Processor.MDC_JOB_ID_KEY); setCache(getExampleResponse); try { final MapPrinter mapPrinter = this.printerFactory.create(appId); if (!checkReferer(request, mapPrinter)) { error(getExampleResponse, "Invalid referer", HttpStatus.FORBIDDEN); return; } final String requestDataPrefix = "requestData"; final File[] children = mapPrinter.getConfiguration().getDirectory().listFiles( (dir, name) -> name.startsWith(requestDataPrefix) && name.endsWith(".json")); if (children == null) { error(getExampleResponse, "Cannot find the config directory", HttpStatus.NOT_FOUND); return; } JSONObject allExamples = new JSONObject(); for (File child: children) { if (child.isFile()) { String requestData = new String(Files.readAllBytes(child.toPath()), Constants.DEFAULT_CHARSET); try { final JSONObject jsonObject = new JSONObject(requestData); jsonObject.remove(JSON_OUTPUT_FORMAT); jsonObject.remove(JSON_APP); requestData = jsonObject.toString(JSON_INDENT_FACTOR); setContentType(getExampleResponse, jsonpCallback); } catch (JSONException e) { // ignore, return raw text } String name = child.getName(); name = name.substring(requestDataPrefix.length()); if (name.startsWith("-")) { name = name.substring(1); } name = FilenameUtils.removeExtension(name); name = name.trim(); if (name.isEmpty()) { name = FilenameUtils.removeExtension(child.getName()); } try { allExamples.put(name, requestData); } catch (JSONException e) { Log.error("Error translating object to json", e); error(getExampleResponse, "Error translating object to json: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); return; } } } final String result; try { result = allExamples.toString(JSON_INDENT_FACTOR); } catch (JSONException e) { Log.error("Error translating object to json", e); error(getExampleResponse, "Error translating object to json: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); return; } try (PrintWriter writer = getExampleResponse.getWriter()) { appendJsonpCallback(jsonpCallback, writer); writer.append(result); appendJsonpCallbackEnd(jsonpCallback, writer); } } catch (NoSuchAppException e) { error(getExampleResponse, "No print app identified by: " + appId, HttpStatus.NOT_FOUND); } } /** * List the available fonts on the system. * * @return the list of available fonts in the system. The result is a JSON Array that just lists the font * family names available. */ @RequestMapping(value = FONTS_URL) @ResponseBody public final String listAvailableFonts() { MDC.remove(Processor.MDC_JOB_ID_KEY); final JSONArray availableFonts = new JSONArray(); final List families = ExtensionsEnvironment.getExtensionsRegistry().getExtensions(FontFamily.class); for (FontFamily family: families) { availableFonts.put(family.getName()); } return availableFonts.toString(); } /** * Maximum time to wait for a createAndGet request to complete before returning an error. * * @param maxCreateAndGetWaitTimeInSeconds the maximum time in seconds to wait for a report to be * generated. */ public final void setMaxCreateAndGetWaitTimeInSeconds(final long maxCreateAndGetWaitTimeInSeconds) { this.maxCreateAndGetWaitTimeInSeconds = maxCreateAndGetWaitTimeInSeconds; } /** * Copy the PDF into the output stream. * * @param metadata the client request data * @param httpServletResponse the response object * @param reportLoader the object used for loading the report * @param reportURI the uri of the report * @param inline whether or not to inline the content */ private void sendReportFile( final PrintJobStatus metadata, final HttpServletResponse httpServletResponse, final ReportLoader reportLoader, final URI reportURI, final boolean inline) throws IOException { try (OutputStream response = httpServletResponse.getOutputStream()) { httpServletResponse.setContentType(metadata.getResult().getMimeType()); if (!inline) { String fileName = metadata.getResult().getFileName(); Matcher matcher = VARIABLE_PATTERN.matcher(fileName); while (matcher.find()) { final String variable = matcher.group(1); String replacement = findReplacement(variable, metadata.getCompletionDate()); fileName = fileName.replace("${" + variable + "}", replacement); matcher = VARIABLE_PATTERN.matcher(fileName); } fileName += "." + metadata.getResult().getFileExtension(); httpServletResponse .setHeader("Content-disposition", "attachment; filename=" + cleanUpName(fileName)); } reportLoader.loadReport(reportURI, response); } } private void addDownloadLinkToJson( final HttpServletRequest httpServletRequest, final String ref, final JSONWriter json) { String downloadURL = getBaseUrl(httpServletRequest) + REPORT_URL + "/" + ref; json.key(JSON_DOWNLOAD_LINK).value(downloadURL); } /** * Read the headers from the request. * * @param httpServletRequest the request object */ protected final JSONObject getHeaders(final HttpServletRequest httpServletRequest) { @SuppressWarnings("rawtypes") Enumeration headersName = httpServletRequest.getHeaderNames(); JSONObject headers = new JSONObject(); while (headersName.hasMoreElements()) { String name = headersName.nextElement().toString(); Enumeration e = httpServletRequest.getHeaders(name); while (e.hasMoreElements()) { headers.append(name, e.nextElement()); } } final JSONObject requestHeadersAttribute = new JSONObject(); requestHeadersAttribute.put(JSON_REQUEST_HEADERS, headers); return requestHeadersAttribute; } /** * Start a print job. * * @param appId the id of the printer app * @param format the format of the returned report. * @param requestDataRaw the request json in string form * @param httpServletRequest the request object * @param httpServletResponse the response object * @return the job reference id */ private String createAndSubmitPrintJob( final String appId, final String format, final String requestDataRaw, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws NoSuchAppException { PJsonObject specJson = parseJson(requestDataRaw, httpServletResponse); if (specJson == null) { return null; } String ref = maybeAddRequestId( UUID.randomUUID().toString() + "@" + this.servletInfo.getServletId(), httpServletRequest); MDC.put(Processor.MDC_JOB_ID_KEY, ref); LOGGER.debug("{}", specJson); specJson.getInternalObj().remove(JSON_OUTPUT_FORMAT); specJson.getInternalObj().put(JSON_OUTPUT_FORMAT, format); specJson.getInternalObj().remove(JSON_APP); specJson.getInternalObj().put(JSON_APP, appId); final JSONObject requestHeaders = getHeaders(httpServletRequest); if (requestHeaders.length() > 0) { specJson.getInternalObj().getJSONObject(JSON_ATTRIBUTES) .put(JSON_REQUEST_HEADERS, requestHeaders); } // check that we have authorization and configure the job so it can only be access by users with // sufficient authorization final String templateName = specJson.getString(Constants.JSON_LAYOUT_KEY); final MapPrinter mapPrinter = this.mapPrinterFactory.create(appId); checkReferer(httpServletRequest, mapPrinter); final Template template = mapPrinter.getConfiguration().getTemplate(templateName); PrintJobEntryImpl jobEntry = new PrintJobEntryImpl(ref, specJson, System.currentTimeMillis()); jobEntry.configureAccess(template, this.context); try { this.jobManager.submit(jobEntry); } catch (RuntimeException exc) { LOGGER.error("Error when creating job on {}: {}", appId, specJson, exc); ref = null; } return ref; } private boolean checkReferer( final HttpServletRequest request, final MapPrinter mapPrinter) { final Configuration config = mapPrinter.getConfiguration(); final UriMatchers allowedReferers = config.getAllowedReferersImpl(); if (allowedReferers == null) { return true; } String referer = request.getHeader("referer"); if (referer == null) { referer = "http://localhost/"; } try { return allowedReferers.matches(new URI(referer), HttpMethod.resolve(request.getMethod())); } catch (SocketException | UnknownHostException | URISyntaxException | MalformedURLException e) { LOGGER.error("Referer {} invalid", referer, e); return false; } } private R loadReport( final String referenceId, final HttpServletResponse httpServletResponse, final HandleReportLoadResult handler) throws IOException, ServletException { PrintJobStatus metadata; try { metadata = this.jobManager.getStatus(referenceId); } catch (NoSuchReferenceException e) { return handler.unknownReference(httpServletResponse, referenceId); } if (!metadata.isDone()) { return handler.printJobPending(httpServletResponse, referenceId); } else if (metadata.getResult() != null) { URI pdfURI = metadata.getResult().getReportURI(); ReportLoader loader = null; for (ReportLoader reportLoader: this.reportLoaders) { if (reportLoader.accepts(pdfURI)) { loader = reportLoader; break; } } if (loader == null) { return handler.unsupportedLoader(httpServletResponse, referenceId); } else { return handler.successfulPrint(metadata, httpServletResponse, pdfURI, loader); } } else { return handler.failedPrint(metadata, httpServletResponse); } } private void setContentType( final HttpServletResponse statusResponse, final String jsonpCallback) { if (StringUtils.isEmpty(jsonpCallback)) { statusResponse.setContentType("application/json; charset=utf-8"); } else { statusResponse.setContentType("application/javascript; charset=utf-8"); } } private void appendJsonpCallback( final String jsonpCallback, final PrintWriter writer) { if (!StringUtils.isEmpty(jsonpCallback)) { writer.append(jsonpCallback); writer.append("("); } } private void appendJsonpCallbackEnd( final String jsonpCallback, final PrintWriter writer) { if (!StringUtils.isEmpty(jsonpCallback)) { writer.append(");"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy