org.mapfish.print.config.Configuration Maven / Gradle / Ivy
Show all versions of print-lib Show documentation
package org.mapfish.print.config;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.Mark;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.Symbolizer;
import org.json.JSONException;
import org.json.JSONWriter;
import org.mapfish.print.Constants;
import org.mapfish.print.config.access.AccessAssertion;
import org.mapfish.print.config.access.AlwaysAllowAssertion;
import org.mapfish.print.config.access.RoleAccessAssertion;
import org.mapfish.print.http.CertificateStore;
import org.mapfish.print.http.HttpCredential;
import org.mapfish.print.http.HttpProxy;
import org.mapfish.print.map.style.StyleParser;
import org.mapfish.print.map.style.json.ColorParser;
import org.mapfish.print.processor.http.matcher.URIMatcher;
import org.mapfish.print.processor.http.matcher.UriMatchers;
import org.mapfish.print.servlet.fileloader.ConfigFileLoaderManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
/**
* The Main Configuration Bean.
*
*/
public class Configuration implements ConfigurationObject {
private static final Map GEOMETRY_NAME_ALIASES;
static {
HashMap map = new HashMap<>();
map.put(Geometry.class.getSimpleName().toLowerCase(), Geometry.class.getSimpleName().toLowerCase());
map.put("geom", Geometry.class.getSimpleName().toLowerCase());
map.put("geometrycollection", Geometry.class.getSimpleName().toLowerCase());
map.put("multigeometry", Geometry.class.getSimpleName().toLowerCase());
map.put("line", LineString.class.getSimpleName().toLowerCase());
map.put(LineString.class.getSimpleName().toLowerCase(),
LineString.class.getSimpleName().toLowerCase());
map.put("linearring", LineString.class.getSimpleName().toLowerCase());
map.put("multilinestring", LineString.class.getSimpleName().toLowerCase());
map.put("multiline", LineString.class.getSimpleName().toLowerCase());
map.put("poly", Polygon.class.getSimpleName().toLowerCase());
map.put(Polygon.class.getSimpleName().toLowerCase(), Polygon.class.getSimpleName().toLowerCase());
map.put("multipolygon", Polygon.class.getSimpleName().toLowerCase());
map.put(Point.class.getSimpleName().toLowerCase(), Point.class.getSimpleName().toLowerCase());
map.put("multipoint", Point.class.getSimpleName().toLowerCase());
map.put(Constants.Style.OverviewMap.NAME, Constants.Style.OverviewMap.NAME);
GEOMETRY_NAME_ALIASES = map;
}
private Map templates;
private File configurationFile;
private Map styles = new HashMap<>();
private Map defaultStyle = new HashMap<>();
private boolean throwErrorOnExtraParameters = true;
private List proxies = Lists.newArrayList();
private PDFConfig pdfConfig = new PDFConfig();
private List credentials = Lists.newArrayList();
private CertificateStore certificateStore;
private OldApiConfig oldApi = new OldApiConfig();
private String outputFilename;
private String resourceBundle;
private boolean defaultToSvg = false;
private Set jdbcDrivers = Sets.newHashSet();
private Map namedStyles = Maps.newHashMap();
private UriMatchers allowedReferers = null;
/**
* The color used to draw the WMS tiles error default: transparent pink.
*/
private String transparentTileErrorColor = "rgba(255, 78, 78, 125)";
/**
* The color used to draw the other tiles error default: pink.
*/
private String opaqueTileErrorColor = "rgba(255, 155, 155, 0)";
@Autowired
private StyleParser styleParser;
@Autowired
private ClientHttpRequestFactory clientHttpRequestFactory;
@Autowired
private ConfigFileLoaderManager fileLoaderManager;
@Autowired
private ApplicationContext context;
private AccessAssertion accessAssertion = AlwaysAllowAssertion.INSTANCE;
final PDFConfig getPdfConfig() {
return this.pdfConfig;
}
/**
* Configure various properties related to the reports generated as PDFs.
*
* @param pdfConfig the pdf configuration
*/
public final void setPdfConfig(final PDFConfig pdfConfig) {
this.pdfConfig = pdfConfig;
}
/**
* Initialize some optionally wired fields.
*/
@PostConstruct
public final void init() {
this.namedStyles = this.context.getBeansOfType(Style.class);
}
/**
* Either use the provided value (renderAsSvg) or if it is null then use {@link #defaultToSvg}.
*
* @param renderAsSvg the value to use if non-null.
*/
public final boolean renderAsSvg(final Boolean renderAsSvg) {
return renderAsSvg == null ? this.defaultToSvg : renderAsSvg;
}
/**
* If true then all vector layers (and other parts of the system that can be either SVG or Bitmap, like
* scalebar) will be rendered as SVG (unless layer specifically indicates useSvg as false).
*
* The default is false.
*
*
* @param defaultToSvg whether or not to create svg layers by default
*/
public final void setDefaultToSvg(final boolean defaultToSvg) {
this.defaultToSvg = defaultToSvg;
}
/**
* The configuration for locating a custom certificate store.
*/
@Nullable
public final CertificateStore getCertificateStore() {
return this.certificateStore;
}
/**
* The configuration for locating a custom certificate store. This is only required if the default
* certificate store which ships with all java installations does not contain the certificates needed by
* this server. Usually it is to accept a self-signed certificate, for example on a test server.
*
* @param certificateStore The configuration for locating a custom certificate store
*/
public final void setCertificateStore(final CertificateStore certificateStore) {
this.certificateStore = certificateStore;
}
/**
* Get the http credentials. Should also getProxies since {@link org.mapfish.print.http.HttpProxy} is a
* subclass of {@link org.mapfish.print.http.HttpCredential}.
*/
public final List getCredentials() {
return this.credentials;
}
/**
* Http credentials to be used when making http requests.
*
* If a proxy needs credentials you don't need to configure it here because the proxy configuration object
* also has options for declaring the credentials.
*
*
* @param credentials the credentials
*/
public final void setCredentials(final List credentials) {
this.credentials = credentials;
}
/**
* Get the http proxies used by in all requests in this syste.
*
* @see org.mapfish.print.http.ConfigFileResolvingHttpRequestFactory
*/
public final List getProxies() {
return this.proxies;
}
/**
* Configuration for proxying http requests. Each proxy can be configured with authentication and with
* the uris that they apply to.
*
* See {@link org.mapfish.print.http.HttpProxy} for details on how to configure them.
*
* @param proxies the proxy configuration objects
*/
public final void setProxies(final List proxies) {
this.proxies = proxies;
}
/**
* Print out the configuration that the client needs to make a request.
*
* @param json the output writer.
* @throws JSONException
*/
public final void printClientConfig(final JSONWriter json) throws JSONException {
json.key("layouts");
json.array();
final Map accessibleTemplates = getTemplates();
for (String name: accessibleTemplates.keySet()) {
json.object();
json.key("name").value(name);
accessibleTemplates.get(name).printClientConfig(json);
json.endObject();
}
json.endArray();
}
public final String getOutputFilename() {
return this.outputFilename;
}
/**
* The default output file name of the report. This can be overridden by {@link
* org.mapfish.print.config.Template#setOutputFilename(String)} and the outputFilename parameter in the
* request JSON.
*
* This can be a string and can also have a date section in the string that will be filled when the report
* is created for example a section with ${<dateFormatString>} will be replaced with the current
* date formatted in the way defined by the <dateFormatString> string. The format rules are the
* rules in
*
* java.text.SimpleDateFormat (do a google search if the link above is broken).
*
*
* Example: outputFilename: print-${dd-MM-yyyy}
should output:
* print-22-11-2014.pdf
*
*
* Note: the suffix will be appended to the end of the name.
*
*
* @param outputFilename default output file name of the report.
*/
public final void setOutputFilename(final String outputFilename) {
this.outputFilename = outputFilename;
}
public final Map getTemplates() {
return Maps.filterEntries(this.templates, new Predicate>() {
@Override
public boolean apply(@Nullable final Map.Entry input) {
if (input == null) {
return false;
}
try {
Configuration.this.accessAssertion.assertAccess("Configuration",
this);
input.getValue().assertAccessible(input.getKey());
return true;
} catch (AccessDeniedException | AuthenticationCredentialsNotFoundException e) {
return false;
}
}
});
}
/**
* Set the configuration of the named template.
*
* @param templates the templates;
*/
public final void setTemplates(final Map templates) {
this.templates = templates;
}
/**
* Retrieve the configuration of the named template.
*
* @param name the template name;
*/
public final Template getTemplate(final String name) {
final Template template = this.templates.get(name);
if (template != null) {
this.accessAssertion.assertAccess("Configuration", this);
template.assertAccessible(name);
} else {
throw new IllegalArgumentException(String.format("Template '%s' does not exist. Options are: " +
"%s", name, this.templates.keySet()));
}
return template;
}
public final File getDirectory() {
return this.configurationFile.getAbsoluteFile().getParentFile();
}
public final void setConfigurationFile(final File configurationFile) {
this.configurationFile = configurationFile;
}
/**
* Set the named styles defined in the configuration for this.
*
* @param styles the style definition. StyleParser plugins will be used to load the style.
*/
public final void setStyles(final Map styles) {
this.styles = styles;
}
/**
* Return the named style ot Optional.absent() if there is not a style with the given name.
*
* @param styleName the name of the style to look up
*/
public final Optional extends Style> getStyle(final String styleName) {
final String styleRef = this.styles.get(styleName);
if (styleRef != null) {
return this.styleParser.loadStyle(this, this.clientHttpRequestFactory, styleRef);
} else {
return Optional.absent();
}
}
/**
* Get a default style. If null a simple black line style will be returned.
*
* @param geometryType the name of the geometry type (point, line, polygon)
*/
@Nonnull
public final Style getDefaultStyle(@Nonnull final String geometryType) {
String normalizedGeomName = GEOMETRY_NAME_ALIASES.get(geometryType.toLowerCase());
if (normalizedGeomName == null) {
normalizedGeomName = geometryType.toLowerCase();
}
Style style = this.defaultStyle.get(normalizedGeomName.toLowerCase());
if (style == null) {
style = this.namedStyles.get(normalizedGeomName.toLowerCase());
}
if (style == null) {
StyleBuilder builder = new StyleBuilder();
final Symbolizer symbolizer;
if (isPointType(normalizedGeomName)) {
symbolizer = builder.createPointSymbolizer();
} else if (isLineType(normalizedGeomName)) {
symbolizer = builder.createLineSymbolizer(Color.black, 2);
} else if (isPolygonType(normalizedGeomName)) {
symbolizer = builder.createPolygonSymbolizer(Color.lightGray, Color.black, 2);
} else if (normalizedGeomName.equalsIgnoreCase(Constants.Style.Raster.NAME)) {
symbolizer = builder.createRasterSymbolizer();
} else if (normalizedGeomName.startsWith(Constants.Style.OverviewMap.NAME)) {
symbolizer = createMapOverviewStyle(normalizedGeomName, builder);
} else {
final Style geomStyle = this.defaultStyle.get(Geometry.class.getSimpleName().toLowerCase());
if (geomStyle != null) {
return geomStyle;
} else {
symbolizer = builder.createPointSymbolizer();
}
}
style = builder.createStyle(symbolizer);
}
return style;
}
private boolean isPolygonType(@Nonnull final String normalizedGeomName) {
return normalizedGeomName.equalsIgnoreCase(Polygon.class.getSimpleName())
|| normalizedGeomName.equalsIgnoreCase(MultiPolygon.class.getSimpleName());
}
private boolean isLineType(@Nonnull final String normalizedGeomName) {
return normalizedGeomName.equalsIgnoreCase(LineString.class.getSimpleName())
|| normalizedGeomName.equalsIgnoreCase(MultiLineString.class.getSimpleName())
|| normalizedGeomName.equalsIgnoreCase(LinearRing.class.getSimpleName());
}
private boolean isPointType(@Nonnull final String normalizedGeomName) {
return normalizedGeomName.equalsIgnoreCase(Point.class.getSimpleName())
|| normalizedGeomName.equalsIgnoreCase(MultiPoint.class.getSimpleName());
}
private Symbolizer createMapOverviewStyle(
@Nonnull final String normalizedGeomName,
@Nonnull final StyleBuilder builder) {
Stroke stroke = builder.createStroke(Color.blue, 2);
final Fill fill = builder.createFill(Color.blue, 0.2);
String overviewGeomType = Polygon.class.getSimpleName();
if (normalizedGeomName.contains(":")) {
final String[] parts = normalizedGeomName.split(":");
overviewGeomType = parts[1];
}
if (isPointType(overviewGeomType)) {
final Mark mark = builder.createMark(StyleBuilder.MARK_CIRCLE, fill, stroke);
Graphic graphic = builder.createGraphic(null, mark, null);
graphic.setSize(builder.literalExpression(Constants.Style.POINT_SIZE));
return builder.createPointSymbolizer(graphic);
}
if (isLineType(overviewGeomType)) {
return builder.createLineSymbolizer(stroke);
}
return builder.createPolygonSymbolizer(stroke, fill);
}
/**
* Set the default styles. the case of the keys are not important. The retrieval will be case
* insensitive.
*
* @param defaultStyle the mapping from geometry type name (point, polygon, etc...) to the style
* to use for that type.
*/
public final void setDefaultStyle(final Map defaultStyle) {
this.defaultStyle = Maps.newHashMapWithExpectedSize(defaultStyle.size());
for (Map.Entry entry: defaultStyle.entrySet()) {
String normalizedName = GEOMETRY_NAME_ALIASES.get(entry.getKey().toLowerCase());
if (normalizedName == null) {
normalizedName = entry.getKey().toLowerCase();
}
this.defaultStyle.put(normalizedName, entry.getValue());
}
}
/**
* If true and the request JSON has extra parameters in the layers definition, exceptions will be thrown.
* Otherwise the information will be logged.
*/
public final boolean isThrowErrorOnExtraParameters() {
return this.throwErrorOnExtraParameters;
}
/**
* If true and the request JSON has extra parameters in the layers definition, exceptions will be thrown.
* Otherwise the information will be logged.
*
* @param throwErrorOnExtraParameters the value
*/
public final void setThrowErrorOnExtraParameters(final boolean throwErrorOnExtraParameters) {
this.throwErrorOnExtraParameters = throwErrorOnExtraParameters;
}
@Override
public void validate(final List validationErrors, final Configuration configuration) {
validationErrors.addAll(validate());
}
/**
* Validate that the configuration is valid.
*
* @return any validation errors.
*/
public final List validate() {
List validationErrors = Lists.newArrayList();
this.accessAssertion.validate(validationErrors, this);
for (String jdbcDriver: this.jdbcDrivers) {
try {
Class.forName(jdbcDriver);
} catch (ClassNotFoundException e) {
try {
Configuration.class.getClassLoader().loadClass(jdbcDriver);
} catch (ClassNotFoundException e1) {
validationErrors.add(new ConfigurationException(String.format(
"Unable to load JDBC driver: %s ensure that the web application has the jar " +
"on its classpath", jdbcDriver)));
}
}
}
if (this.configurationFile == null) {
validationErrors.add(new ConfigurationException("Configuration file is field on configuration " +
"object is null"));
}
if (this.templates.isEmpty()) {
validationErrors.add(new ConfigurationException("There are not templates defined."));
}
for (Template template: this.templates.values()) {
template.validate(validationErrors, this);
}
for (HttpProxy proxy: this.proxies) {
proxy.validate(validationErrors, this);
}
try {
ColorParser.toColor(this.opaqueTileErrorColor);
} catch (RuntimeException ex) {
validationErrors.add(new ConfigurationException("Cannot parse opaqueTileErrorColor", ex));
}
try {
ColorParser.toColor(this.transparentTileErrorColor);
} catch (RuntimeException ex) {
validationErrors.add(new ConfigurationException("Cannot parse transparentTileErrorColor", ex));
}
return validationErrors;
}
/**
* check if the file exists and can be accessed by the user/template/config/etc...
*
* @param pathToSubResource a string representing a file that is accessible for use in printing
* templates within the configuration file. In the case of a file based URI the path could be a
* relative path (relative to the configuration file) or an absolute path, but it must be an
* allowed file (you can't allow access to any file on the file system).
*/
public final boolean isAccessible(final String pathToSubResource) throws IOException {
return this.fileLoaderManager.isAccessible(this.configurationFile.toURI(), pathToSubResource);
}
/**
* Load the file related to the configuration file.
*
* @param pathToSubResource a string representing a file that is accessible for use in printing
* templates within the configuration file. In the case of a file based URI the path could be a
* relative path (relative to the configuration file) or an absolute path, but it must be an
* allowed file (you can't allow access to any file on the file system).
*/
public final byte[] loadFile(final String pathToSubResource) throws IOException {
return this.fileLoaderManager.loadFile(this.configurationFile.toURI(), pathToSubResource);
}
/**
* Set file loader manager.
*
* @param fileLoaderManager new manager.
*/
public final void setFileLoaderManager(final ConfigFileLoaderManager fileLoaderManager) {
this.fileLoaderManager = fileLoaderManager;
}
/**
* Set the JDBC drivers that are required to connect to the databases in the configuration. JDBC drivers
* are needed (for example) when database sources are used in templates. For example if in one of the
* template you have:
*
*
* jdbcUrl: "jdbc:postgresql://localhost:5432/morges_dpfe"
*
*
* then you need to add:
*
*
* jdbcDrivers: [org.postgresql.Driver]
*
*
*
* or
*
*
* jdbcDrivers:
* - org.postgresql.Driver
*
*
* @param jdbcDrivers the set of JDBC drivers to load before performing a print (this ensures they
* are registered with the JVM)
*/
public final void setJdbcDrivers(final Set jdbcDrivers) {
this.jdbcDrivers = jdbcDrivers;
}
/**
* The roles required to access this configuration/app. If empty or not set then it is a
* public app. If there are many roles then a user must have one of the roles in order to
* access the configuration/app.
*
* The security (how authentication/authorization is done) is configured in the
* /WEB-INF/classes/mapfish-spring-security.xml
*
* Any user without the required role will get an error when trying to access any of the templates and no
* templates will be listed in the capabilities requests.
*
*
* @param access the roles needed to access this
*/
public final void setAccess(final ArrayList access) {
final RoleAccessAssertion assertion = new RoleAccessAssertion();
assertion.setRequiredRoles(access);
this.accessAssertion = assertion;
}
public final AccessAssertion getAccessAssertion() {
return this.accessAssertion;
}
/**
* Get the configuration options on how to interpret the request in the form of the old API.
*/
public final OldApiConfig getOldApi() {
return this.oldApi;
}
/**
* Set the configuration options on how to interpret the request in the form of the old API.
*
* @param oldApi the old api configuration object
*/
public final void setOldApi(final OldApiConfig oldApi) {
this.oldApi = oldApi;
}
/**
* Get the color used to draw the WMS tiles error default: transparent pink.
*/
public final String getTransparentTileErrorColor() {
return this.transparentTileErrorColor;
}
/**
* Color used for tiles in error on transparent layers.
*
* @param transparentTileErrorColor The color
*/
public final void setTransparentTileErrorColor(final String transparentTileErrorColor) {
this.transparentTileErrorColor = transparentTileErrorColor;
}
/**
* Get the color used to draw the other tiles error default: pink.
*/
public final String getOpaqueTileErrorColor() {
return this.opaqueTileErrorColor;
}
/**
* Color used for tiles in error on opaque layers.
*
* @param opaqueTileErrorColor The color
*/
public final void setOpaqueTileErrorColor(final String opaqueTileErrorColor) {
this.opaqueTileErrorColor = opaqueTileErrorColor;
}
/**
* Get the resource bundle name.
*/
public final String getResourceBundle() {
return this.resourceBundle;
}
/**
* Set the resource bundle name.
*
* @param resourceBundle the resource bundle name
*/
public final void setResourceBundle(final String resourceBundle) {
this.resourceBundle = resourceBundle;
}
/**
* @return the list of referer checks (null = no check)
*/
public final UriMatchers getAllowedReferersImpl() {
return this.allowedReferers;
}
/**
* The matchers used to authorize the incoming requests in function of the referer. For example:
*
* allowedReferers:
* - !hostnameMatch
* host: example.com
* allowSubDomains: true
*
*
* By default, the referer is not checked
*
* @param matchers the list of matcher to use to check if a referer is permitted or null for no
* check
* @see org.mapfish.print.processor.http.matcher.URIMatcher
*/
public final void setAllowedReferers(@Nullable final List extends URIMatcher> matchers) {
this.allowedReferers = matchers != null ? new UriMatchers(matchers) : null;
}
}