org.mapfish.print.servlet.job.PrintJob Maven / Gradle / Ivy
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.config.access.AccessAssertion;
import org.mapfish.print.config.access.AndAccessAssertion;
import org.mapfish.print.output.OutputFormat;
import org.mapfish.print.servlet.MapPrinterServlet;
import org.mapfish.print.servlet.NoSuchAppException;
import org.mapfish.print.servlet.ServletMapPrinterFactory;
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.Date;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/**
* The information for printing a report.
*
* @author Jesse
*/
public abstract class PrintJob implements Callable {
private static final Logger LOGGER = LoggerFactory.getLogger(PrintJob.class);
private String referenceId;
private PJsonObject requestData;
private AccessAssertion access;
private Long createTime;
@Autowired
private MapPrinterFactory mapPrinterFactory;
@Autowired
private MetricRegistry metricRegistry;
@Autowired
private ApplicationContext applicationContext;
private SecurityContext securityContext;
/**
* Get the reference id of the job so it can be looked up again later.
*/
public final String getReferenceId() {
return this.referenceId;
}
/**
* Set the reference id of the job so it can be looked up again later.
*
* @param referenceId the referenceId
*/
public final void setReferenceId(final String referenceId) {
this.referenceId = referenceId;
}
/*
* The timestamp when the job was created.
*/
public final Long getCreateTime() {
return this.createTime;
}
/*
* The date when the job was created.
*/
public final Date getCreateTimeAsDate() {
return new Date(this.createTime);
}
/*
* Set the timestamp when the job was created.
*/
public final void setCreateTime(final Long createTime) {
this.createTime = createTime;
}
/**
* Set the data from the client making the request.
*
* @param requestData the json data
*/
public final void setRequestData(final PJsonObject requestData) {
this.requestData = requestData;
}
/**
* 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 Throwable;
@Override
public final PrintJobStatus 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.referenceId);
spec = PrintJob.this.requestData;
mapPrinter = PrintJob.this.mapPrinterFactory.create(getAppId());
final MapPrinter finalMapPrinter = mapPrinter;
URI reportURI = withOpenOutputStream(new PrintAction() {
@Override
public void run(final OutputStream outputStream) throws Throwable {
finalMapPrinter.print(PrintJob.this.requestData, outputStream);
}
});
this.metricRegistry.counter(getClass().getName() + "success").inc();
LOGGER.info("Successfully completed print job " + this.referenceId);
LOGGER.debug("Job " + this.referenceId + "\n" + this.requestData);
String fileName = getFileName(mapPrinter, spec);
final OutputFormat outputFormat = mapPrinter.getOutputFormat(spec);
String mimeType = outputFormat.getContentType();
String fileExtension = outputFormat.getFileSuffix();
return new SuccessfulPrintJob(this.referenceId, reportURI, getAppId(), getCreateTimeAsDate(), new Date(), 0L,
fileName, mimeType, fileExtension, this.access);
} catch (Throwable e) {
String canceledText = "";
if (Thread.currentThread().isInterrupted()) {
canceledText = "(canceled) ";
}
LOGGER.info("Error executing print job " + canceledText + this.referenceId + "\n" + this.requestData, e);
this.metricRegistry.counter(getClass().getName() + "failure").inc();
String fileName = "unknownFileName";
if (spec != null) {
fileName = getFileName(mapPrinter, spec);
}
final Throwable rootCause = getRootCause(e);
return new FailedPrintJob(this.referenceId, getAppId(), getCreateTimeAsDate(), new Date(), 0L,
fileName, rootCause.toString(), false, this.access);
} finally {
final long stop = TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS);
LOGGER.debug("Print Job " + PrintJob.this.referenceId + " completed in " + stop + "ms");
}
}
/**
* Because exceptions might get re-thrown several times, an error message like
* "java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: ..."
* might get created. To avoid this, this method finds the root cause, so that only a message like
* "java.lang.IllegalArgumentException: ..." is shown.
*/
private Throwable getRootCause(final Throwable e) {
Throwable rootCause = e;
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
rootCause = rootCause.getCause();
}
return rootCause;
}
protected final String getAppId() {
return PrintJob.this.requestData.optString(
MapPrinterServlet.JSON_APP,
ServletMapPrinterFactory.DEFAULT_CONFIGURATION_FILE_KEY);
}
/**
* 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());
}
public final AccessAssertion getAccess() {
return this.access;
}
/**
* Configure the access permissions required to access this print job.
*
* @param template the containing print template which should have sufficient information to configure the access.
*/
public final void configureAccess(final Template template) {
final Configuration configuration = template.getConfiguration();
AndAccessAssertion accessAssertion = this.applicationContext.getBean(AndAccessAssertion.class);
accessAssertion.setPredicates(configuration.getAccessAssertion(), template.getAccessAssertion());
this.access = accessAssertion;
}
final void initForTesting(final ApplicationContext context) {
this.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 getAppIds() {
return null;
}
};
}
/**
* 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 Throwable;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy