package org.mapfish.print.servlet.job; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import org.mapfish.print.Constants; 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.output.OutputFormat; import org.mapfish.print.servlet.NoSuchAppException; import org.mapfish.print.servlet.job.impl.PrintJobEntryImpl; import org.mapfish.print.servlet.job.impl.PrintJobResultImpl; import org.mapfish.print.wrapper.json.PJsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import java.io.OutputStream; import java.net.URI; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; /** * The information for printing a report. */ public abstract class PrintJob implements Callable<PrintJobResult> { private static final Logger LOGGER = LoggerFactory.getLogger(PrintJob.class); private PrintJobEntry entry; @Autowired private MapPrinterFactory mapPrinterFactory; @Autowired private MetricRegistry metricRegistry; private SecurityContext securityContext; public final PrintJobEntry getEntry() { return this.entry; } public final void setEntry(final PrintJobEntry entry) { this.entry = entry; } /** * Open an OutputStream and execute the function using the OutputStream. * * @param function the function to execute * @return the */ protected abstract URI withOpenOutputStream(PrintAction function) throws Exception; /** * Create Print Job Result. * * @param reportURI the report URI * @param fileName the file name * @param fileExtension the file extension * @param mimeType the mime type * @return the job result */ //CHECKSTYLE:OFF protected PrintJobResult createResult(final URI reportURI, final String fileName, final String fileExtension, final String mimeType) { //CHECKSTYLE:ON return new PrintJobResultImpl(reportURI, fileName, fileExtension, mimeType); } @Override public final PrintJobResult call() throws Exception { SecurityContextHolder.setContext(this.securityContext); Timer.Context timer = this.metricRegistry.timer(getClass().getName() + " call()").time(); PJsonObject spec = null; MapPrinter mapPrinter = null; try { LOGGER.info("Starting print job " + this.entry.getReferenceId()); spec = this.entry.getRequestData(); mapPrinter = PrintJob.this.mapPrinterFactory.create(this.entry.getAppId()); final MapPrinter finalMapPrinter = mapPrinter; URI reportURI = withOpenOutputStream(new PrintAction() { @Override public void run(final OutputStream outputStream) throws Exception { finalMapPrinter.print(PrintJob.this.entry.getRequestData(), outputStream); } }); this.metricRegistry.counter(getClass().getName() + "success").inc(); LOGGER.info("Successfully completed print job " + this.entry.getReferenceId()); LOGGER.debug("Job " + this.entry.getReferenceId() + "\n" + this.entry.getRequestData()); String fileName = getFileName(mapPrinter, spec); String mimeType = null; String fileExtension = null; if (mapPrinter != null) { //can only happen in test final OutputFormat outputFormat = mapPrinter.getOutputFormat(spec); mimeType = outputFormat.getContentType(); fileExtension = outputFormat.getFileSuffix(); } return createResult(reportURI, fileName, fileExtension, mimeType); } catch (Exception e) { String canceledText = ""; if (Thread.currentThread().isInterrupted()) { canceledText = "(canceled) "; } LOGGER.info("Error executing print job " + canceledText + this.entry.getReferenceId() + "\n" + this.entry.getRequestData(), e); this.metricRegistry.counter(getClass().getName() + "failure").inc(); throw e; } finally { final long stop = TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS); LOGGER.debug("Print Job " + this.entry.getReferenceId() + " completed in " + stop + "ms"); } } /** * Read filename from spec. */ private static String getFileName(@Nullable final MapPrinter mapPrinter, final PJsonObject spec) { String fileName = spec.optString(Constants.OUTPUT_FILENAME_KEY); if (fileName != null) { return fileName; } if (mapPrinter != null) { final Configuration config = mapPrinter.getConfiguration(); final String templateName = spec.getString(Constants.JSON_LAYOUT_KEY); final Template template = config.getTemplate(templateName); if (template.getOutputFilename() != null) { return template.getOutputFilename(); } if (config.getOutputFilename() != null) { return config.getOutputFilename(); } } return "mapfish-print-report"; } /** * The security context that contains the information about the user that made the request. This must be * set on {@link org.springframework.security.core.context.SecurityContextHolder} when the thread starts executing. * * @param securityContext the conext object */ public final void setSecurityContext(final SecurityContext securityContext) { this.securityContext = SecurityContextHolder.createEmptyContext(); this.securityContext.setAuthentication(securityContext.getAuthentication()); } final void initForTesting(final ApplicationContext context) { this.metricRegistry = context.getBean(MetricRegistry.class); this.mapPrinterFactory = new MapPrinterFactory() { @Override public MapPrinter create(final String app) throws NoSuchAppException { return null; } @Override public Set<String> getAppIds() { return null; } }; this.entry = new PrintJobEntryImpl(); } /** * Interface encapsulating the code to run with the open output stream. */ protected interface PrintAction { /** * Execute the action. * * @param outputStream the output stream to write the report to. */ void run(OutputStream outputStream) throws Exception; } }